From 882e1f95bf5da5aa954ad1ab357e6bba3688e4e0 Mon Sep 17 00:00:00 2001 From: joaquimg Date: Sat, 12 Apr 2025 00:56:21 -0700 Subject: [PATCH 01/11] Better names --- src/constrained_variables.jl | 4 +++ src/structures.jl | 60 ++++++++++++++++++++++++++---------- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/src/constrained_variables.jl b/src/constrained_variables.jl index 7b9dfdca..1f79c632 100644 --- a/src/constrained_variables.jl +++ b/src/constrained_variables.jl @@ -50,6 +50,8 @@ function _add_constrained_variables( for ci in cis f = MOI.get(primal_model, MOI.ConstraintFunction(), ci) if all( + # no element of the VectorOfVariables is a constrained variable + # and not a parameter vi -> !haskey(m.constrained_var_idx, vi) && !(vi in params), f.variables, ) @@ -76,6 +78,8 @@ function _add_constrained_variable( ) for ci in cis f = MOI.get(primal_model, MOI.ConstraintFunction(), ci) + # no element of the VectorOfVariables is a constrained variable + # and not a parameter if !haskey(m.constrained_var_idx, f) && !(f in params) set = MOI.get(primal_model, MOI.ConstraintSet(), ci) if !iszero(MOI.constant(set)) diff --git a/src/structures.jl b/src/structures.jl index 4f458f22..80697488 100644 --- a/src/structures.jl +++ b/src/structures.jl @@ -30,50 +30,76 @@ MOI.Utilities.@model( Maps information from all structures of the primal to the dual model. +Main user maps: + * `constrained_var_idx::Dict{MOI.VariableIndex,Tuple{MOI.ConstraintIndex,Int}}`: - maps original primal constrained variables to their primal original + maps primal constrained variables to their primal constraints (the special ones that makes them constrained variables) and - their internal index (if vector constraints, VectorOfVariables-in-Set), 1 - otherwise (VariableIndex-in-Set). + their internal index from 1 to dimension(set) (if vector constraints: + VectorOfVariables-in-Set), 1 otherwise (scalar: VariableIndex-in-Set). + # primal_convar_to_primal_convarcon_and_index * `constrained_var_dual::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}`: maps - the original primal constraint index of constrained variables to the dual - model's constraint index of the associated dual constraint. + 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_convarcon_to_dual_con - * `constrained_var_zero::Dict{MOI.ConstraintIndex,Unions{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`. + note: from the above two maps, we can get primal_convar_to_dual_con_and_index * `primal_var_dual_con::Dict{MOI.VariableIndex,MOI.ConstraintIndex}`: maps - "free" primal variables to their associated dual constraints. Free variables - as opposed to constrained variables. Note that Dualization will select - automatically which variables are free and which are constrained. + "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_var_to_dual_con + + note: from the above three maps, we can get primal_var_to_dual_con_and_index * `primal_con_dual_var::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. + # primal_con_to_dual_var_vec * `primal_con_dual_con::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}`: maps - primal constraints to dual variable constraints (if there is such constraint - the dual dual variable is said to be constrained). If the primal + 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). + 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. + # primal_con_to_dual_convarcon + + Additional helper maps: * `primal_con_constants::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 constrains a constant (`EqualtTo`, `GreaterThan`, `LessThan`). + # primal_con_to_primal_constants_vec * `primal_parameter::Dict{MOI.VariableIndex,MOI.VariableIndex}`: maps parameters in the primal to parameters in the dual model. + # primal_parameter_to_dual_parameter + + * `constrained_var_zero::Dict{MOI.ConstraintIndex,Unions{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_convarcon_to_dual_function * `primal_var_dual_quad_slack::Dict{MOI.VariableIndex,MOI.VariableIndex}`: maps primal variables (that appear in quadratic objective terms) to dual - "slack" variables. + "slack" variables. These primal variables might appear in other maps. + # primal_var_in_quad_obj_to_dual_slack_var """ mutable struct PrimalDualMap{T} constrained_var_idx::Dict{MOI.VariableIndex,Tuple{MOI.ConstraintIndex,Int}} From 114fe2df2b14d7f8a4ae9e1cfd9d0ad8f7e5369e Mon Sep 17 00:00:00 2001 From: joaquimg Date: Sat, 12 Apr 2025 22:23:51 -0700 Subject: [PATCH 02/11] rm hashtags --- src/structures.jl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/structures.jl b/src/structures.jl index 80697488..029198d3 100644 --- a/src/structures.jl +++ b/src/structures.jl @@ -37,7 +37,7 @@ Main user maps: 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). - # primal_convar_to_primal_convarcon_and_index + Future name: primal_convar_to_primal_convarcon_and_index * `constrained_var_dual::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}`: maps the primal constraint index of constrained variables to the dual @@ -45,8 +45,8 @@ Main user maps: 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_convarcon_to_dual_con + primal_convarcon_to_dual_function). + Future name: primal_convarcon_to_dual_con note: from the above two maps, we can get primal_convar_to_dual_con_and_index @@ -55,7 +55,7 @@ Main user maps: Free variables as opposed to constrained variables. Note that Dualization will select automatically which variables are free and which are constrained. - # primal_var_to_dual_con + Future name: primal_var_to_dual_con note: from the above three maps, we can get primal_var_to_dual_con_and_index @@ -64,7 +64,7 @@ Main user maps: scalar constraints those vectors will be single element vectors. Primal Constrained variables constraints (the main ones) are not in this map. - # primal_con_to_dual_var_vec + Future name: primal_con_to_dual_var_vec * `primal_con_dual_con::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}`: maps primal constraints to dual constrained variable. If the primal @@ -73,7 +73,7 @@ Main user maps: 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. - # primal_con_to_dual_convarcon + Future name: primal_con_to_dual_convarcon Additional helper maps: @@ -82,11 +82,11 @@ Main user maps: This map is used in `MOI.get(::DualOptimizer,::MOI.ConstraintPrimal,ci)` that requires extra information in the case that the scalar set constrains a constant (`EqualtTo`, `GreaterThan`, `LessThan`). - # primal_con_to_primal_constants_vec + Future name: primal_con_to_primal_constants_vec * `primal_parameter::Dict{MOI.VariableIndex,MOI.VariableIndex}`: maps parameters in the primal to parameters in the dual model. - # primal_parameter_to_dual_parameter + Future name: primal_parameter_to_dual_parameter * `constrained_var_zero::Dict{MOI.ConstraintIndex,Unions{MOI.ScalarAffineFunction,MOI.VectorAffineFunction}}`: caches scalar affine functions or vector affine functions associated with @@ -94,12 +94,12 @@ Main user maps: `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_convarcon_to_dual_function + Future name: primal_convarcon_to_dual_function * `primal_var_dual_quad_slack::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. - # primal_var_in_quad_obj_to_dual_slack_var + Future name: primal_var_in_quad_obj_to_dual_slack_var """ mutable struct PrimalDualMap{T} constrained_var_idx::Dict{MOI.VariableIndex,Tuple{MOI.ConstraintIndex,Int}} From 80b27b3486cb432f7c9849522a11d47149f473e1 Mon Sep 17 00:00:00 2001 From: joaquimg Date: Mon, 14 Apr 2025 17:38:01 -0700 Subject: [PATCH 03/11] apply rename --- src/MOI_wrapper.jl | 50 ++++---- src/constrained_variables.jl | 12 +- src/dual_equality_constraints.jl | 52 ++++---- src/dual_model_variables.jl | 58 ++++----- src/dualize.jl | 14 +-- src/objective_coefficients.jl | 12 +- src/structures.jl | 144 ++++++++++++++-------- test/Tests/test_dual_names.jl | 16 +-- test/Tests/test_dualize_conic_linear.jl | 38 +++--- test/Tests/test_dualize_exponential.jl | 16 +-- test/Tests/test_dualize_power.jl | 14 +-- test/Tests/test_dualize_quadratic.jl | 22 ++-- test/Tests/test_dualize_rsoc.jl | 10 +- test/Tests/test_dualize_sdp.jl | 14 +-- test/Tests/test_dualize_soc.jl | 14 +-- test/Tests/test_partial_dual_linear.jl | 30 ++--- test/Tests/test_partial_dual_quadratic.jl | 60 ++++----- test/Tests/test_structures.jl | 2 +- test/optimize_abstract_models.jl | 2 +- 19 files changed, 312 insertions(+), 268 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 2d1d8135..0586158c 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -159,15 +159,15 @@ function MOI.modify( constant = -constant end vi = obj_change.variable - if vi in keys(primal_dual_map.constrained_var_idx) - ci_primal, index = primal_dual_map.constrained_var_idx[vi] - ci_dual = primal_dual_map.constrained_var_dual[ci_primal] + if vi in keys(primal_dual_map.primal_convar_to_primal_convarcon_and_index) + 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_dual_con[vi] + ci_dual = primal_dual_map.primal_var_to_dual_con[vi] index = 1 end _change_constant( @@ -237,11 +237,11 @@ end # MOI.get auxiliary functions function get_ci_dual_problem(optimizer::DualOptimizer, vi::MOI.VariableIndex) - return optimizer.dual_problem.primal_dual_map.primal_var_dual_con[vi] + return optimizer.dual_problem.primal_dual_map.primal_var_to_dual_con[vi] end function get_ci_dual_problem(optimizer::DualOptimizer, ci::MOI.ConstraintIndex) - return optimizer.dual_problem.primal_dual_map.primal_con_dual_con[ci] + return optimizer.dual_problem.primal_dual_map.primal_con_to_dual_convarcon[ci] end function get_primal_ci_constant( @@ -255,7 +255,7 @@ function get_primal_ci_constants( optimizer::DualOptimizer, ci::MOI.ConstraintIndex, ) - return optimizer.dual_problem.primal_dual_map.primal_con_constants[ci] + return optimizer.dual_problem.primal_dual_map.primal_con_to_primal_constants_vec[ci] end function get_vi_dual_problem(optimizer::DualOptimizer, ci::MOI.ConstraintIndex) @@ -263,7 +263,7 @@ function get_vi_dual_problem(optimizer::DualOptimizer, ci::MOI.ConstraintIndex) end function get_vis_dual_problem(optimizer::DualOptimizer, ci::MOI.ConstraintIndex) - return optimizer.dual_problem.primal_dual_map.primal_con_dual_var[ci] + return optimizer.dual_problem.primal_dual_map.primal_con_to_dual_var_vec[ci] end function MOI.get(optimizer::DualOptimizer, ::MOI.SolverName) @@ -296,7 +296,7 @@ function _get( ::MOI.ConstraintIndex{Nothing,Nothing}, ) where {T} n = MOI.output_dimension( - optimizer.dual_problem.primal_dual_map.constrained_var_zero[ci_primal], + optimizer.dual_problem.primal_dual_map.primal_convarcon_to_dual_function[ci_primal], ) return zeros(T, n) end @@ -328,9 +328,9 @@ function MOI.get( vi::MOI.VariableIndex, ) primal_dual_map = optimizer.dual_problem.primal_dual_map - if vi in keys(primal_dual_map.constrained_var_idx) - ci_primal, idx = primal_dual_map.constrained_var_idx[vi] - ci_dual = primal_dual_map.constrained_var_dual[ci_primal] + if vi in keys(primal_dual_map.primal_convar_to_primal_convarcon_and_index) + 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] return _get_at_index( optimizer, MOI.ConstraintDual(), @@ -353,11 +353,11 @@ 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 ci in keys(primal_dual_map.constrained_var_dual) - ci_dual = primal_dual_map.constrained_var_dual[ci] + if ci in keys(primal_dual_map.primal_convarcon_to_dual_con) + ci_dual = primal_dual_map.primal_convarcon_to_dual_con[ci] if ci_dual === NO_CONSTRAINT return MOI.Utilities.eval_variables( - primal_dual_map.constrained_var_zero[ci], + primal_dual_map.primal_convarcon_to_dual_function[ci], ) do vi return MOI.get( optimizer.dual_problem.dual_model, @@ -391,11 +391,11 @@ 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 ci in keys(primal_dual_map.constrained_var_dual) - ci_dual = primal_dual_map.constrained_var_dual[ci] + if ci in keys(primal_dual_map.primal_convarcon_to_dual_con) + ci_dual = primal_dual_map.primal_convarcon_to_dual_con[ci] if ci_dual === NO_CONSTRAINT return MOI.Utilities.eval_variables( - primal_dual_map.constrained_var_zero[ci], + primal_dual_map.primal_convarcon_to_dual_function[ci], ) do vi return MOI.get( optimizer.dual_problem.dual_model, @@ -407,7 +407,7 @@ function MOI.get( return MOI.get( optimizer.dual_problem.dual_model, MOI.ConstraintPrimal(), - primal_dual_map.constrained_var_dual[ci], + primal_dual_map.primal_convarcon_to_dual_con[ci], ) else return MOI.get.( @@ -424,17 +424,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 ci in keys(primal_dual_map.constrained_var_dual) + if ci in keys(primal_dual_map.primal_convarcon_to_dual_con) return _get( optimizer, MOI.ConstraintDual(), ci, - primal_dual_map.constrained_var_dual[ci], + primal_dual_map.primal_convarcon_to_dual_con[ci], ) else primal_ci_constant = get_primal_ci_constant(optimizer, ci) # If it has no key then there is no dual constraint - if !haskey(primal_dual_map.primal_con_dual_con, ci) + if !haskey(primal_dual_map.primal_con_to_dual_convarcon, ci) return -primal_ci_constant end ci_dual_problem = get_ci_dual_problem(optimizer, ci) @@ -452,16 +452,16 @@ 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 ci in keys(primal_dual_map.constrained_var_dual) + if ci in keys(primal_dual_map.primal_convarcon_to_dual_con) return _get( optimizer, MOI.ConstraintDual(), ci, - primal_dual_map.constrained_var_dual[ci], + primal_dual_map.primal_convarcon_to_dual_con[ci], ) else # If it has no key then there is no dual constraint - if !haskey(primal_dual_map.primal_con_dual_con, ci) + if !haskey(primal_dual_map.primal_con_to_dual_convarcon, ci) # The number of dual variable associated with the primal constraint is the ci dimension ci_dimension = length(get_vis_dual_problem(optimizer, ci)) return zeros(T, ci_dimension) diff --git a/src/constrained_variables.jl b/src/constrained_variables.jl index 1f79c632..e4517d83 100644 --- a/src/constrained_variables.jl +++ b/src/constrained_variables.jl @@ -52,15 +52,15 @@ function _add_constrained_variables( if all( # no element of the VectorOfVariables is a constrained variable # and not a parameter - vi -> !haskey(m.constrained_var_idx, vi) && !(vi in params), + vi -> !haskey(m.primal_convar_to_primal_convarcon_and_index, vi) && !(vi in params), f.variables, ) for (i, vi) in enumerate(f.variables) - m.constrained_var_idx[vi] = (ci, i) + 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.constrained_var_dual[ci] = NO_CONSTRAINT + m.primal_convarcon_to_dual_con[ci] = NO_CONSTRAINT end end return @@ -80,15 +80,15 @@ function _add_constrained_variable( f = MOI.get(primal_model, MOI.ConstraintFunction(), ci) # no element of the VectorOfVariables is a constrained variable # and not a parameter - if !haskey(m.constrained_var_idx, f) && !(f in params) + if !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.constrained_var_idx[f] = (ci, 1) + 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.constrained_var_dual[ci] = NO_CONSTRAINT + m.primal_convarcon_to_dual_con[ci] = NO_CONSTRAINT end end return diff --git a/src/dual_equality_constraints.jl b/src/dual_equality_constraints.jl index d63c78d6..29d13a51 100644 --- a/src/dual_equality_constraints.jl +++ b/src/dual_equality_constraints.jl @@ -26,7 +26,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_dual_var, + primal_dual_map.primal_con_to_dual_var_vec, all_variables, con_types, T, @@ -42,7 +42,7 @@ function add_dual_equality_constraints( # TODO: unflip these signs add_scalar_affine_terms_from_quad_obj( scalar_affine_terms, - primal_dual_map.primal_var_dual_quad_slack, + primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var, primal_objective, sense_change, ) @@ -52,13 +52,13 @@ function add_dual_equality_constraints( # TODO: unflip these signs add_scalar_affine_terms_from_quad_params( scalar_affine_terms, - primal_dual_map.primal_parameter, + primal_dual_map.primal_parameter_to_dual_parameter, primal_objective, sense_change, ) # Constrained variables - for ci in keys(primal_dual_map.constrained_var_dual) + for ci in keys(primal_dual_map.primal_convarcon_to_dual_con) # 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 @@ -67,8 +67,8 @@ function add_dual_equality_constraints( _add_constrained_variable_constraint( dual_model, primal_model, - primal_dual_map.constrained_var_zero, - primal_dual_map.constrained_var_dual, + primal_dual_map.primal_convarcon_to_dual_function, + primal_dual_map.primal_convarcon_to_dual_con, ci, scalar_affine_terms, scalar_terms, @@ -80,7 +80,7 @@ function add_dual_equality_constraints( # Free variables for primal_vi in non_parameter_variables - if primal_vi in keys(primal_dual_map.constrained_var_idx) + if primal_vi in keys(primal_dual_map.primal_convar_to_primal_convarcon_and_index) continue # constrained variable end # Add equality constraint @@ -109,7 +109,7 @@ function add_dual_equality_constraints( ) end # Add primal variable to dual contraint to the link dictionary - push!(primal_dual_map.primal_var_dual_con, primal_vi => dual_ci) + push!(primal_dual_map.primal_var_to_dual_con, primal_vi => dual_ci) end return scalar_affine_terms end @@ -240,26 +240,26 @@ function add_scalar_affine_terms_from_quad_obj( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_var_dual_quad_slack::Dict{MOI.VariableIndex,MOI.VariableIndex}, + primal_var_in_quad_obj_to_dual_slack_var::Dict{MOI.VariableIndex,MOI.VariableIndex}, primal_objective::PrimalObjective{T}, sense_change::T, ) where {T} for term in primal_objective.obj.quadratic_terms if term.variable_1 == term.variable_2 - dual_vi = primal_var_dual_quad_slack[term.variable_1] + dual_vi = primal_var_in_quad_obj_to_dual_slack_var[term.variable_1] push_to_scalar_affine_terms!( scalar_affine_terms[term.variable_1], -sense_change * MOI.coefficient(term), dual_vi, ) else - dual_vi_1 = primal_var_dual_quad_slack[term.variable_1] + dual_vi_1 = primal_var_in_quad_obj_to_dual_slack_var[term.variable_1] push_to_scalar_affine_terms!( scalar_affine_terms[term.variable_2], -sense_change * MOI.coefficient(term), dual_vi_1, ) - dual_vi_2 = primal_var_dual_quad_slack[term.variable_2] + dual_vi_2 = primal_var_in_quad_obj_to_dual_slack_var[term.variable_2] push_to_scalar_affine_terms!( scalar_affine_terms[term.variable_1], -sense_change * MOI.coefficient(term), @@ -275,13 +275,13 @@ function add_scalar_affine_terms_from_quad_params( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_parameter::Dict{MOI.VariableIndex,MOI.VariableIndex}, + primal_parameter_to_dual_parameter::Dict{MOI.VariableIndex,MOI.VariableIndex}, primal_objective::PrimalObjective{T}, sense_change::T, ) where {T} for (key, val) in primal_objective.quad_cross_parameters for term in val - dual_vi = primal_parameter[term.variable] + dual_vi = primal_parameter_to_dual_parameter[term.variable] push_to_scalar_affine_terms!( scalar_affine_terms[key], -sense_change * MOI.coefficient(term), @@ -324,7 +324,7 @@ function fill_scalar_affine_terms!( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_con_dual_var::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}, + primal_con_to_dual_var_vec::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}, primal_model::MOI.ModelLike, ::Type{F}, ::Type{S}, @@ -332,7 +332,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_dual_var, + primal_con_to_dual_var_vec, primal_model, ci, ) @@ -342,7 +342,7 @@ end function get_scalar_affine_terms( primal_model::MOI.ModelLike, - primal_con_dual_var::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}, + primal_con_to_dual_var_vec::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}, variables::Vector{MOI.VariableIndex}, con_types::Vector{Tuple{Type,Type}}, ::Type{T}, @@ -354,7 +354,7 @@ function get_scalar_affine_terms( for (F, S) in con_types fill_scalar_affine_terms!( scalar_affine_terms, - primal_con_dual_var, + primal_con_to_dual_var_vec, primal_model, F, S, @@ -379,13 +379,13 @@ function fill_scalar_affine_terms!( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_con_dual_var::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}, + primal_con_to_dual_var_vec::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}, 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 = get_function(primal_model, ci) for term in moi_function.terms - dual_vi = primal_con_dual_var[ci][1] # In this case we only have one vi + dual_vi = primal_con_to_dual_var_vec[ci][1] # In this case we only have one vi push_to_scalar_affine_terms!( scalar_affine_terms[term.variable], MOI.coefficient(term), @@ -400,11 +400,11 @@ function fill_scalar_affine_terms!( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_con_dual_var::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}, + primal_con_to_dual_var_vec::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}, 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_dual_var, ci, nothing) + dual_var = get(primal_con_to_dual_var_vec, ci, nothing) if dual_var === nothing # No variables created as the primal constraint is the constraint # of a constrained variable. Hence, its duality information goes to @@ -426,14 +426,14 @@ function fill_scalar_affine_terms!( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_con_dual_var::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}, + primal_con_to_dual_var_vec::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}, primal_model::MOI.ModelLike, ci::MOI.ConstraintIndex{MOI.VectorAffineFunction{T},S}, ) where {T,S<:MOI.AbstractVectorSet} moi_function = get_function(primal_model, ci) set = get_set(primal_model, ci) for term in moi_function.terms - dual_vi = primal_con_dual_var[ci][term.output_index] + dual_vi = primal_con_to_dual_var_vec[ci][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!( @@ -450,11 +450,11 @@ function fill_scalar_affine_terms!( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_con_dual_var::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}, + primal_con_to_dual_var_vec::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}, primal_model::MOI.ModelLike, ci::MOI.ConstraintIndex{MOI.VectorOfVariables,S}, ) where {T,S<:MOI.AbstractVectorSet} - dual_vars = get(primal_con_dual_var, ci, nothing) + dual_vars = get(primal_con_to_dual_var_vec, ci, nothing) if dual_vars === nothing # No variables created as the primal constraint is the constraint # of a constrained variable. Hence, its duality information goes to diff --git a/src/dual_model_variables.jl b/src/dual_model_variables.jl index ab43a11b..2437b3e5 100644 --- a/src/dual_model_variables.jl +++ b/src/dual_model_variables.jl @@ -15,28 +15,28 @@ function _add_dual_vars_in_dual_cones( for ci in MOI.get(primal_model, MOI.ListOfConstraintIndices{F,S}()) # If `F` not one of these two, we can skip the `in` check. if (F === MOI.VectorOfVariables || F === MOI.VariableIndex) && - haskey(primal_dual_map.constrained_var_dual, ci) + haskey(primal_dual_map.primal_convarcon_to_dual_con, ci) continue end # Add dual variable to dual cone # Fill a dual objective dictionary - # Fill the primal_con_dual_var dictionary + # Fill the primal_con_to_dual_var_vec dictionary ci_dual = add_dual_variable( dual_model, primal_model, dual_names, - primal_dual_map.primal_con_dual_var, + primal_dual_map.primal_con_to_dual_var_vec, dual_obj_affine_terms, ci, ) - push_to_primal_con_dual_con!( - primal_dual_map.primal_con_dual_con, + push_to_primal_con_to_dual_convarcon!( + primal_dual_map.primal_con_to_dual_convarcon, ci, ci_dual, ) - push_to_primal_con_constants!( + push_to_primal_con_to_primal_constants_vec!( primal_model, - primal_dual_map.primal_con_constants, + primal_dual_map.primal_con_to_primal_constants_vec, ci, ) end @@ -65,19 +65,19 @@ function add_dual_vars_in_dual_cones( return dual_obj_affine_terms end -# Utils for primal_con_constants dict -function push_to_primal_con_constants!( +# Utils for primal_con_to_primal_constants_vec dict +function push_to_primal_con_to_primal_constants_vec!( primal_model::MOI.ModelLike, - primal_con_constants::Dict{MOI.ConstraintIndex,Vector{T}}, + primal_con_to_primal_constants_vec::Dict{MOI.ConstraintIndex,Vector{T}}, ci::MOI.ConstraintIndex{F,S}, ) where {T,F<:MOI.AbstractScalarFunction,S<:MOI.AbstractScalarSet} - push!(primal_con_constants, ci => get_scalar_term(primal_model, ci)) + push!(primal_con_to_primal_constants_vec, ci => get_scalar_term(primal_model, ci)) return end -function push_to_primal_con_constants!( +function push_to_primal_con_to_primal_constants_vec!( primal_model::MOI.ModelLike, - primal_con_constants::Dict{MOI.ConstraintIndex,Vector{T}}, + primal_con_to_primal_constants_vec::Dict{MOI.ConstraintIndex,Vector{T}}, ci::MOI.ConstraintIndex{F,S}, ) where {T,F<:MOI.AbstractVectorFunction,S<:MOI.AbstractVectorSet} # No constants need to be passed to the DualOptimizer in this case, @@ -87,18 +87,18 @@ function push_to_primal_con_constants!( return end -# Utils for primal_con_dual_con dict -function push_to_primal_con_dual_con!( - primal_con_dual_con::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}, +# Utils for primal_con_to_dual_convarcon dict +function push_to_primal_con_to_dual_convarcon!( + primal_con_to_dual_convarcon::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}, ci::MOI.ConstraintIndex, ci_dual::MOI.ConstraintIndex, ) - push!(primal_con_dual_con, ci => ci_dual) + push!(primal_con_to_dual_convarcon, ci => ci_dual) return end -function push_to_primal_con_dual_con!( - primal_con_dual_con::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}, +function push_to_primal_con_to_dual_convarcon!( + primal_con_to_dual_convarcon::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}, ci::MOI.ConstraintIndex, ci_dual::Nothing, ) @@ -136,13 +136,13 @@ function add_dual_variable( dual_model::MOI.ModelLike, primal_model::MOI.ModelLike, dual_names::DualNames, - primal_con_dual_var::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}, + primal_con_to_dual_var_vec::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}, dual_obj_affine_terms::Dict{MOI.VariableIndex,T}, ci::MOI.ConstraintIndex{F,S}, ) 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 - push!(primal_con_dual_var, ci => vis) + push!(primal_con_to_dual_var_vec, ci => vis) # Get constraint name ci_name = MOI.get(primal_model, MOI.ConstraintName(), ci) # Add each vi to the dictionary @@ -205,8 +205,8 @@ function add_primal_parameter_vars( else push!(added, ind) vi = MOI.add_variable(dual_model) - push_to_primal_parameter!( - primal_dual_map.primal_parameter, + push_to_primal_parameter_to_dual_parameter!( + primal_dual_map.primal_parameter_to_dual_parameter, ind, vi, ) @@ -226,8 +226,8 @@ function add_primal_parameter_vars( elseif length(variable_parameters) > 0 vis = MOI.add_variables(dual_model, length(variable_parameters)) for i in eachindex(vis) - push_to_primal_parameter!( - primal_dual_map.primal_parameter, + push_to_primal_parameter_to_dual_parameter!( + primal_dual_map.primal_parameter_to_dual_parameter, variable_parameters[i], vis[i], ) @@ -250,12 +250,12 @@ function add_primal_parameter_vars( end # Save mapping between primal parameter and dual parameter -function push_to_primal_parameter!( - primal_parameter::Dict{MOI.VariableIndex,MOI.VariableIndex}, +function push_to_primal_parameter_to_dual_parameter!( + primal_parameter_to_dual_parameter::Dict{MOI.VariableIndex,MOI.VariableIndex}, vi::MOI.VariableIndex, vi_dual::MOI.VariableIndex, ) - push!(primal_parameter, vi => vi_dual) + push!(primal_parameter_to_dual_parameter, vi => vi_dual) return end @@ -291,7 +291,7 @@ function add_quadratic_slack_vars( push!(added, ind) vi = MOI.add_variable(dual_model) push_to_quad_slack!( - primal_dual_map.primal_var_dual_quad_slack, + primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var, ind, vi, ) diff --git a/src/dualize.jl b/src/dualize.jl index ae2adf4c..467048e8 100644 --- a/src/dualize.jl +++ b/src/dualize.jl @@ -89,9 +89,9 @@ function dualize( get_primal_objective(primal_model, variable_parameters, T) # Cache information of which primal variables are `constrained_variables` - # creating a map: constrained_var_idx, from original primal vars to original + # 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: `constrained_var_dual`, from original primal ci + # 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 @@ -112,14 +112,14 @@ function dualize( # * creates the dual variable associated with the primal constraint # * fills `dual_obj_affine_terms`, since we are already looping through # all constraints that might have constants. - # * fills `primal_con_dual_var` mapping the primal constraint and the dual + # * fills `primal_con_to_dual_var_vec` mapping the primal constraint and the dual # variable - # * fills `primal_con_dual_con` to map the primal constraint to a + # * fills `primal_con_to_dual_convarcon` to map the primal constraint to a # constraint in the dual variable (if there is such constraint the dual # dual variable is said to be constrained). 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). - # * fills `primal_con_constants` mapping primal constraints to their + # * fills `primal_con_to_primal_constants_vec` mapping 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 @@ -134,7 +134,7 @@ function dualize( # Creates variables in the dual problem that represent parameters in the # primal model. - # Fills `primal_parameter` mapping parameters in the primal to parameters + # Fills `primal_parameter_to_dual_parameter` mapping parameters in the primal to parameters # in the dual model. add_primal_parameter_vars( dual_problem.dual_model, @@ -149,7 +149,7 @@ function dualize( # Add dual slack variables that are associated to the primal quadratic terms # All primal variables that appear in the objective products will have an # associated dual slack variable that is created here. - # also, `primal_var_dual_quad_slack` is filled, mapping primal variables + # also, `primal_var_in_quad_obj_to_dual_slack_var` is filled, mapping primal variables # (that appear in quadritc objective terms) to dual "slack" variables. add_quadratic_slack_vars( dual_problem.dual_model, diff --git a/src/objective_coefficients.jl b/src/objective_coefficients.jl index be5c04ba..97e17964 100644 --- a/src/objective_coefficients.jl +++ b/src/objective_coefficients.jl @@ -256,8 +256,8 @@ function get_dual_objective( quad_terms, MOI.ScalarQuadraticTerm{T}( -MOI.coefficient(term), - map.primal_var_dual_quad_slack[term.variable_1], - map.primal_var_dual_quad_slack[term.variable_2], + map.primal_var_in_quad_obj_to_dual_slack_var[term.variable_1], + map.primal_var_in_quad_obj_to_dual_slack_var[term.variable_2], ), ) end @@ -275,7 +275,7 @@ function get_dual_objective( lin_terms, MOI.ScalarAffineTerm{T}( MOI.coefficient(term), - map.primal_parameter[term.variable], + map.primal_parameter_to_dual_parameter[term.variable], ), ) end @@ -288,8 +288,8 @@ function get_dual_objective( quad_terms, MOI.ScalarQuadraticTerm{T}( MOI.coefficient(term), - map.primal_parameter[term.variable_1], - map.primal_parameter[term.variable_2], + map.primal_parameter_to_dual_parameter[term.variable_1], + map.primal_parameter_to_dual_parameter[term.variable_2], ), ) end @@ -301,7 +301,7 @@ function get_dual_objective( # and, thus, the go to the obj of the dual. # TODO? set_dot for vi in variable_parameters - param = map.primal_parameter[vi] + param = map.primal_parameter_to_dual_parameter[vi] for term in scalar_affine_terms[vi] push!( quad_terms, diff --git a/src/structures.jl b/src/structures.jl index 029198d3..4390295d 100644 --- a/src/structures.jl +++ b/src/structures.jl @@ -30,132 +30,176 @@ MOI.Utilities.@model( Maps information from all structures of the primal to the dual model. -Main user maps: +The following abbreviations are used in the maps: - * `constrained_var_idx::Dict{MOI.VariableIndex,Tuple{MOI.ConstraintIndex,Int}}`: +* `var`: variable index +* `con`: constraint index +* `convar`: constrained variable, variable index +* `convarcon`: constrained variable, constraint index + +Main 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). - Future name: primal_convar_to_primal_convarcon_and_index - * `constrained_var_dual::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}`: maps + 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 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). - Future name: primal_convarcon_to_dual_con note: from the above two maps, we can get primal_convar_to_dual_con_and_index - * `primal_var_dual_con::Dict{MOI.VariableIndex,MOI.ConstraintIndex}`: maps + * `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. - Future name: primal_var_to_dual_con note: from the above three maps, we can get primal_var_to_dual_con_and_index - * `primal_con_dual_var::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}`: + # 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. Primal Constrained variables constraints (the main ones) are not in this map. - Future name: primal_con_to_dual_var_vec - * `primal_con_dual_con::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}`: maps + note: possibly change this to + + * `primal_con_to_dual_convarcon::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}`: maps 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). 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. - Future name: primal_con_to_dual_convarcon Additional helper maps: - * `primal_con_constants::Dict{MOI.ConstraintIndex,Vector{T}}`: maps primal + * `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 constrains a constant (`EqualtTo`, `GreaterThan`, `LessThan`). - Future name: primal_con_to_primal_constants_vec + # TODO is the vec ever really used??? - * `primal_parameter::Dict{MOI.VariableIndex,MOI.VariableIndex}`: maps + * `primal_parameter_to_dual_parameter::Dict{MOI.VariableIndex,MOI.VariableIndex}`: maps parameters in the primal to parameters in the dual model. - Future name: primal_parameter_to_dual_parameter - * `constrained_var_zero::Dict{MOI.ConstraintIndex,Unions{MOI.ScalarAffineFunction,MOI.VectorAffineFunction}}`: + * `primal_convarcon_to_dual_function::Dict{MOI.ConstraintIndex,Unions{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`. - Future name: primal_convarcon_to_dual_function - * `primal_var_dual_quad_slack::Dict{MOI.VariableIndex,MOI.VariableIndex}`: + * `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} - constrained_var_idx::Dict{MOI.VariableIndex,Tuple{MOI.ConstraintIndex,Int}} - constrained_var_dual::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex} - constrained_var_zero::Dict{ + 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{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_dual_con::Dict{MOI.VariableIndex,MOI.ConstraintIndex} - primal_con_dual_var::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}} - primal_con_dual_con::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex} - primal_con_constants::Dict{MOI.ConstraintIndex,Vector{T}} - - primal_parameter::Dict{MOI.VariableIndex,MOI.VariableIndex} - primal_var_dual_quad_slack::Dict{MOI.VariableIndex,MOI.VariableIndex} + primal_var_in_quad_obj_to_dual_slack_var::Dict{MOI.VariableIndex,MOI.VariableIndex} function PrimalDualMap{T}() where {T} return new( Dict{MOI.VariableIndex,Tuple{MOI.ConstraintIndex,Int}}(), Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}(), - Dict{ - MOI.ConstraintIndex, - Union{MOI.VectorAffineFunction{T},MOI.ScalarAffineFunction{T}}, - }(), Dict{MOI.VariableIndex,MOI.ConstraintIndex}(), 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 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) + 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) + 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) + 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) + 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) + 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) + 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) + else + return getfield(m, name) + end +end + function is_empty(primal_dual_map::PrimalDualMap{T}) where {T} - return isempty(primal_dual_map.constrained_var_idx) && - isempty(primal_dual_map.constrained_var_dual) && - isempty(primal_dual_map.constrained_var_zero) && - isempty(primal_dual_map.primal_var_dual_con) && - isempty(primal_dual_map.primal_con_dual_var) && - isempty(primal_dual_map.primal_con_dual_con) && - isempty(primal_dual_map.primal_con_constants) && - isempty(primal_dual_map.primal_parameter) && - isempty(primal_dual_map.primal_var_dual_quad_slack) + return 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_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.constrained_var_idx) - Base.empty!(primal_dual_map.constrained_var_dual) - Base.empty!(primal_dual_map.constrained_var_zero) - Base.empty!(primal_dual_map.primal_var_dual_con) - Base.empty!(primal_dual_map.primal_con_dual_var) - Base.empty!(primal_dual_map.primal_con_dual_con) - Base.empty!(primal_dual_map.primal_con_constants) - Base.empty!(primal_dual_map.primal_parameter) - Base.empty!(primal_dual_map.primal_var_dual_quad_slack) + 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_parameter_to_dual_parameter) + Base.empty!(primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var) return end diff --git a/test/Tests/test_dual_names.jl b/test/Tests/test_dual_names.jl index 0fd6bfaf..e9b45f51 100644 --- a/test/Tests/test_dual_names.jl +++ b/test/Tests/test_dual_names.jl @@ -32,13 +32,13 @@ dual_model = dual_problem.dual_model primal_dual_map = dual_problem.primal_dual_map # Query variable names - vi_1 = primal_dual_map.primal_con_dual_var[MOI.ConstraintIndex{ + vi_1 = primal_dual_map.primal_con_to_dual_var_vec[MOI.ConstraintIndex{ MOI.VariableIndex, MOI.GreaterThan{Float64}, }( 1, )][1] - vi_2 = primal_dual_map.primal_con_dual_var[MOI.ConstraintIndex{ + vi_2 = primal_dual_map.primal_con_to_dual_var_vec[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}, }( @@ -47,8 +47,8 @@ @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_dual_con[MOI.VariableIndex(1)] - ci_2 = primal_dual_map.primal_var_dual_con[MOI.VariableIndex(2)] + 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)] @test MOI.get(dual_model, MOI.ConstraintName(), ci_1) == "" @test MOI.get(dual_model, MOI.ConstraintName(), ci_2) == "" @@ -59,13 +59,13 @@ dual_model = dual_problem.dual_model primal_dual_map = dual_problem.primal_dual_map # Query variable names - vi_1 = primal_dual_map.primal_con_dual_var[MOI.ConstraintIndex{ + vi_1 = primal_dual_map.primal_con_to_dual_var_vec[MOI.ConstraintIndex{ MOI.VariableIndex, MOI.GreaterThan{Float64}, }( 1, )][1] - vi_2 = primal_dual_map.primal_con_dual_var[MOI.ConstraintIndex{ + vi_2 = primal_dual_map.primal_con_to_dual_var_vec[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}, }( @@ -73,8 +73,8 @@ )][1] @test MOI.get(dual_model, MOI.VariableName(), vi_2) == "dualvar_lessthan_1" # Query constraint names - ci_1 = primal_dual_map.primal_var_dual_con[MOI.VariableIndex(1)] - ci_2 = primal_dual_map.primal_var_dual_con[MOI.VariableIndex(2)] + 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)] @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 be8e34d4..141e3edd 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_dual_var = primal_dual_map.primal_con_dual_var + primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec ci_zero = first( MOI.get( primal_model, @@ -65,7 +65,7 @@ }(), ), ) - @test primal_con_dual_var[ci_zero] == MOI.VariableIndex.(1:2) + @test primal_con_to_dual_var_vec[ci_zero] == MOI.VariableIndex.(1:2) ci_nneg = first( MOI.get( primal_model, @@ -75,13 +75,13 @@ }(), ), ) - @test !haskey(primal_con_dual_var, ci_nneg) - @test primal_dual_map.constrained_var_dual[ci_nneg] == ci + @test !haskey(primal_con_to_dual_var_vec, ci_nneg) + @test primal_dual_map.primal_convarcon_to_dual_con[ci_nneg] == ci for i in 1:3 vi = MOI.VariableIndex(i) - @test !haskey(primal_dual_map.primal_var_dual_con, vi) - @test primal_dual_map.constrained_var_idx[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) end end @@ -187,14 +187,14 @@ @test MOI.coefficient.(eq_con3_fun.terms) == -[1.0] @test MOI.constant(eq_con3_fun) == [-4.0] - primal_con_dual_var = primal_dual_map.primal_con_dual_var + 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_dual_var, ci_zero) + @test !haskey(primal_con_to_dual_var_vec, ci_zero) ci_nneg = first( MOI.get( primal_model, @@ -204,8 +204,8 @@ }(), ), ) - @test !haskey(primal_con_dual_var, ci_nneg) - @test primal_dual_map.constrained_var_dual[ci_nneg] == ci_nn + @test !haskey(primal_con_to_dual_var_vec, ci_nneg) + @test primal_dual_map.primal_convarcon_to_dual_con[ci_nneg] == ci_nn ci_npos = first( MOI.get( primal_model, @@ -215,8 +215,8 @@ }(), ), ) - @test !haskey(primal_con_dual_var, ci_npos) - @test primal_dual_map.constrained_var_dual[ci_npos] == ci_np + @test !haskey(primal_con_to_dual_var_vec, ci_npos) + @test primal_dual_map.primal_convarcon_to_dual_con[ci_npos] == ci_np ci_aff_zero = first( MOI.get( primal_model, @@ -226,19 +226,19 @@ }(), ), ) - @test primal_con_dual_var[ci_aff_zero] == MOI.VariableIndex.(1:3) + @test primal_con_to_dual_var_vec[ci_aff_zero] == MOI.VariableIndex.(1:3) - primal_var_dual_con = primal_dual_map.primal_var_dual_con - @test primal_var_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_dual_con, vi) + @test !haskey(primal_var_to_dual_con, vi) end - @test primal_dual_map.constrained_var_idx[MOI.VariableIndex(2)] == + @test primal_dual_map.primal_convar_to_primal_convarcon_and_index[MOI.VariableIndex(2)] == (ci_npos, 1) - @test primal_dual_map.constrained_var_idx[MOI.VariableIndex(3)] == + @test primal_dual_map.primal_convar_to_primal_convarcon_and_index[MOI.VariableIndex(3)] == (ci_nneg, 1) - @test primal_dual_map.constrained_var_idx[MOI.VariableIndex(4)] == + @test primal_dual_map.primal_convar_to_primal_convarcon_and_index[MOI.VariableIndex(4)] == (ci_zero, 1) end end diff --git a/test/Tests/test_dualize_exponential.jl b/test/Tests/test_dualize_exponential.jl index 0a88e089..0e8aee9f 100644 --- a/test/Tests/test_dualize_exponential.jl +++ b/test/Tests/test_dualize_exponential.jl @@ -125,19 +125,19 @@ ) @test dual_exp_con.variables == MOI.VariableIndex.(3:5) - primal_con_dual_var = primal_dual_map.primal_con_dual_var - @test primal_con_dual_var[eq_con1] == [MOI.VariableIndex(1)] - @test primal_con_dual_var[eq_con2] == [MOI.VariableIndex(2)] - @test primal_con_dual_var[MOI.ConstraintIndex{ + 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{ MOI.VectorAffineFunction{Float64}, MOI.ExponentialCone, }( 1, )] == MOI.VariableIndex.(3:5) - primal_var_dual_con = primal_dual_map.primal_var_dual_con - @test primal_var_dual_con[MOI.VariableIndex(1)] == eq_con1 - @test primal_var_dual_con[MOI.VariableIndex(2)] == eq_con2 - @test primal_var_dual_con[MOI.VariableIndex(3)] == eq_con3 + 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 end end diff --git a/test/Tests/test_dualize_power.jl b/test/Tests/test_dualize_power.jl index 5b1b5769..2d79afd5 100644 --- a/test/Tests/test_dualize_power.jl +++ b/test/Tests/test_dualize_power.jl @@ -125,13 +125,13 @@ dual_pow_con = MOI.get(dual_model, MOI.ConstraintFunction(), pow_con) - primal_con_dual_var = primal_dual_map.primal_con_dual_var - @test primal_con_dual_var[eq_con1] == [MOI.VariableIndex(1)] - @test primal_con_dual_var[eq_con2] == [MOI.VariableIndex(2)] + 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_var_dual_con = primal_dual_map.primal_var_dual_con - @test primal_var_dual_con[MOI.VariableIndex(1)] == eq_con1 - @test primal_var_dual_con[MOI.VariableIndex(2)] == eq_con2 - @test primal_var_dual_con[MOI.VariableIndex(3)] == eq_con3 + 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 end end diff --git a/test/Tests/test_dualize_quadratic.jl b/test/Tests/test_dualize_quadratic.jl index a65eaaf0..7228f451 100644 --- a/test/Tests/test_dualize_quadratic.jl +++ b/test/Tests/test_dualize_quadratic.jl @@ -124,46 +124,46 @@ @test MOI.constant.(eq_con3_fun) == 0.0 @test MOI.constant(eq_con3_set) == 0.0 - primal_con_dual_var = primal_dual_map.primal_con_dual_var - @test primal_con_dual_var[MOI.ConstraintIndex{ + primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec + @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, }( 1, )] == [MOI.VariableIndex(1)] - @test primal_con_dual_var[MOI.ConstraintIndex{ + @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, }( 2, )] == [MOI.VariableIndex(2)] - primal_var_dual_con = primal_dual_map.primal_var_dual_con - @test primal_var_dual_con[MOI.VariableIndex(1)] == MOI.ConstraintIndex{ + primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con + @test primal_var_to_dual_con[MOI.VariableIndex(1)] == MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 1, ) - @test primal_var_dual_con[MOI.VariableIndex(2)] == MOI.ConstraintIndex{ + @test primal_var_to_dual_con[MOI.VariableIndex(2)] == MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 2, ) - @test primal_var_dual_con[MOI.VariableIndex(3)] == MOI.ConstraintIndex{ + @test primal_var_to_dual_con[MOI.VariableIndex(3)] == MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 3, ) - primal_var_dual_quad_slack = primal_dual_map.primal_var_dual_quad_slack - @test primal_var_dual_quad_slack[MOI.VariableIndex(1)] == + primal_var_in_quad_obj_to_dual_slack_var = primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var + @test primal_var_in_quad_obj_to_dual_slack_var[MOI.VariableIndex(1)] == MOI.VariableIndex(2 + 1) - @test primal_var_dual_quad_slack[MOI.VariableIndex(2)] == + @test primal_var_in_quad_obj_to_dual_slack_var[MOI.VariableIndex(2)] == MOI.VariableIndex(2 + 2) - @test primal_var_dual_quad_slack[MOI.VariableIndex(3)] == + @test primal_var_in_quad_obj_to_dual_slack_var[MOI.VariableIndex(3)] == MOI.VariableIndex(2 + 3) end @testset "qp2_test" begin diff --git a/test/Tests/test_dualize_rsoc.jl b/test/Tests/test_dualize_rsoc.jl index d8a16d5a..8f11a196 100644 --- a/test/Tests/test_dualize_rsoc.jl +++ b/test/Tests/test_dualize_rsoc.jl @@ -125,16 +125,16 @@ ) @test rsoc_con.variables == MOI.VariableIndex.(1:4) - primal_con_dual_var = primal_dual_map.primal_con_dual_var - @test primal_con_dual_var[MOI.ConstraintIndex{ + primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec + @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone, }( 1, )] == MOI.VariableIndex.(1:4) - primal_var_dual_con = primal_dual_map.primal_var_dual_con - @test primal_var_dual_con[MOI.VariableIndex(1)] == eq_con1 - @test primal_var_dual_con[MOI.VariableIndex(2)] == eq_con2 + 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 end end diff --git a/test/Tests/test_dualize_sdp.jl b/test/Tests/test_dualize_sdp.jl index 9bec7416..0afb2f75 100644 --- a/test/Tests/test_dualize_sdp.jl +++ b/test/Tests/test_dualize_sdp.jl @@ -125,18 +125,18 @@ sdp_con = MOI.get(dual_model, MOI.ConstraintFunction(), spd_con) - primal_con_dual_var = primal_dual_map.primal_con_dual_var - @test primal_con_dual_var[eq_con1] == [MOI.VariableIndex(1)] - @test primal_con_dual_var[MOI.ConstraintIndex{ + 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{ MOI.VectorAffineFunction{Float64}, MOI.PositiveSemidefiniteConeTriangle, }( 1, )] == MOI.VariableIndex.(2:4) - primal_var_dual_con = primal_dual_map.primal_var_dual_con - @test primal_var_dual_con[MOI.VariableIndex(1)] == eq_con1 - @test primal_var_dual_con[MOI.VariableIndex(2)] == eq_con2 - @test primal_var_dual_con[MOI.VariableIndex(3)] == eq_con3 + 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 end end diff --git a/test/Tests/test_dualize_soc.jl b/test/Tests/test_dualize_soc.jl index 130a7d32..91b0129b 100644 --- a/test/Tests/test_dualize_soc.jl +++ b/test/Tests/test_dualize_soc.jl @@ -114,8 +114,8 @@ ) @test soc_con.variables == MOI.VariableIndex.(2:4) - primal_con_dual_var = primal_dual_map.primal_con_dual_var - @test primal_con_dual_var[MOI.ConstraintIndex{ + primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec + @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ MOI.VectorAffineFunction{Float64}, MOI.Zeros, }( @@ -130,12 +130,12 @@ }(), ), ) - @test primal_con_dual_var[primal_soc_con] == + @test primal_con_to_dual_var_vec[primal_soc_con] == [MOI.VariableIndex(2); MOI.VariableIndex(3); MOI.VariableIndex(4)] - primal_var_dual_con = primal_dual_map.primal_var_dual_con - @test primal_var_dual_con[MOI.VariableIndex(1)] == eq_con1 - @test primal_var_dual_con[MOI.VariableIndex(2)] == eq_con2 - @test primal_var_dual_con[MOI.VariableIndex(3)] == eq_con3 + 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 end end diff --git a/test/Tests/test_partial_dual_linear.jl b/test/Tests/test_partial_dual_linear.jl index 6e4bb0a6..4451b759 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_dual_var = primal_dual_map.primal_con_dual_var - @test primal_con_dual_var[MOI.ConstraintIndex{ + primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec + @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}, }( 1, )] == [MOI.VariableIndex(1)] - primal_var_dual_con = primal_dual_map.primal_var_dual_con - @test primal_var_dual_con[MOI.VariableIndex(1)] == MOI.ConstraintIndex{ + primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con + @test primal_var_to_dual_con[MOI.VariableIndex(1)] == MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( @@ -167,7 +167,7 @@ @test MOI.constant.(eq_con2_fun) == 0.0 @test MOI.constant(eq_con2_set) == 3.0 - primal_con_dual_var = primal_dual_map.primal_con_dual_var + primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec vaf_npos, = MOI.get( primal_model, MOI.ListOfConstraintIndices{ @@ -175,7 +175,7 @@ MOI.Nonpositives, }(), ) - @test primal_con_dual_var[vaf_npos] == + @test primal_con_to_dual_var_vec[vaf_npos] == [MOI.VariableIndex(1); MOI.VariableIndex(2)] vgt, = MOI.get( primal_model, @@ -184,10 +184,10 @@ MOI.GreaterThan{Float64}, }(), ) - @test primal_con_dual_var[vgt] == [MOI.VariableIndex(3)] + @test primal_con_to_dual_var_vec[vgt] == [MOI.VariableIndex(3)] - primal_var_dual_con = primal_dual_map.primal_var_dual_con - @test isempty(primal_var_dual_con) + primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con + @test isempty(primal_var_to_dual_con) end @testset "lp12_test - x_1 and x_3 ignored" begin @@ -265,28 +265,28 @@ @test MOI.constant.(eq_con2_fun) == 0.0 @test MOI.constant(eq_con2_set) == 0.0 - primal_con_dual_var = primal_dual_map.primal_con_dual_var - @test primal_con_dual_var[MOI.ConstraintIndex{ + primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec + @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ MOI.VariableIndex, MOI.LessThan{Float64}, }( 2, )] == [MOI.VariableIndex(3)] - @test primal_con_dual_var[MOI.ConstraintIndex{ + @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ MOI.VariableIndex, MOI.LessThan{Float64}, }( 1, )] == [MOI.VariableIndex(2)] - @test primal_con_dual_var[MOI.ConstraintIndex{ + @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}, }( 1, )] == [MOI.VariableIndex(1)] - primal_var_dual_con = primal_dual_map.primal_var_dual_con - @test primal_var_dual_con[MOI.VariableIndex(2)] == MOI.ConstraintIndex{ + primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con + @test primal_var_to_dual_con[MOI.VariableIndex(2)] == 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 ed52270c..60a27c26 100644 --- a/test/Tests/test_partial_dual_quadratic.jl +++ b/test/Tests/test_partial_dual_quadratic.jl @@ -105,41 +105,41 @@ @test MOI.constant.(eq_con2_fun) == 0.0 @test MOI.constant(eq_con2_set) == 0.0 - primal_con_dual_var = primal_dual_map.primal_con_dual_var - @test primal_con_dual_var[MOI.ConstraintIndex{ + primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec + @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, }( 1, )] == [MOI.VariableIndex(1)] - @test primal_con_dual_var[MOI.ConstraintIndex{ + @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, }( 2, )] == [MOI.VariableIndex(2)] - primal_var_dual_con = primal_dual_map.primal_var_dual_con - @test primal_var_dual_con[MOI.VariableIndex(1)] == MOI.ConstraintIndex{ + primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con + @test primal_var_to_dual_con[MOI.VariableIndex(1)] == MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 1, ) - @test primal_var_dual_con[MOI.VariableIndex(2)] == MOI.ConstraintIndex{ + @test primal_var_to_dual_con[MOI.VariableIndex(2)] == MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 2, ) - primal_parameter = primal_dual_map.primal_parameter - @test primal_parameter[MOI.VariableIndex(3)] == MOI.VariableIndex(2 + 1) + primal_parameter_to_dual_parameter = primal_dual_map.primal_parameter_to_dual_parameter + @test primal_parameter_to_dual_parameter[MOI.VariableIndex(3)] == MOI.VariableIndex(2 + 1) - primal_var_dual_quad_slack = primal_dual_map.primal_var_dual_quad_slack - @test primal_var_dual_quad_slack[MOI.VariableIndex(1)] == + primal_var_in_quad_obj_to_dual_slack_var = primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var + @test primal_var_in_quad_obj_to_dual_slack_var[MOI.VariableIndex(1)] == MOI.VariableIndex(2 + 1 + 1) - @test primal_var_dual_quad_slack[MOI.VariableIndex(2)] == + @test primal_var_in_quad_obj_to_dual_slack_var[MOI.VariableIndex(2)] == MOI.VariableIndex(2 + 1 + 2) end @testset "qp2_test - ignore y" begin @@ -218,32 +218,32 @@ @test MOI.constant.(eq_con1_fun) == 0.0 @test MOI.constant(eq_con1_set) == -1.0 - primal_con_dual_var = primal_dual_map.primal_con_dual_var - @test primal_con_dual_var[MOI.ConstraintIndex{ + primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec + @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 1, )] == [MOI.VariableIndex(1)] @test !haskey( - primal_con_dual_var, + primal_con_to_dual_var_vec, MOI.ConstraintIndex{MOI.VariableIndex,MOI.GreaterThan{Float64}}(1), ) - @test primal_con_dual_var[MOI.ConstraintIndex{ + @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ MOI.VariableIndex, MOI.GreaterThan{Float64}, }( 2, )] == [MOI.VariableIndex(2)] - primal_var_dual_con = primal_dual_map.primal_var_dual_con - @test isempty(primal_var_dual_con) + primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con + @test isempty(primal_var_to_dual_con) - primal_parameter = primal_dual_map.primal_parameter - @test primal_parameter[MOI.VariableIndex(2)] == MOI.VariableIndex(3) + primal_parameter_to_dual_parameter = primal_dual_map.primal_parameter_to_dual_parameter + @test primal_parameter_to_dual_parameter[MOI.VariableIndex(2)] == MOI.VariableIndex(3) - primal_var_dual_quad_slack = primal_dual_map.primal_var_dual_quad_slack - @test primal_var_dual_quad_slack[MOI.VariableIndex(1)] == + primal_var_in_quad_obj_to_dual_slack_var = primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var + @test primal_var_in_quad_obj_to_dual_slack_var[MOI.VariableIndex(1)] == MOI.VariableIndex(4) end @testset "qp2_test - ignore y - no obj" begin @@ -322,31 +322,31 @@ @test MOI.constant.(eq_con1_fun) == 0.0 @test MOI.constant(eq_con1_set) == -1.0 - primal_con_dual_var = primal_dual_map.primal_con_dual_var - @test primal_con_dual_var[MOI.ConstraintIndex{ + primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec + @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 1, )] == [MOI.VariableIndex(1)] @test !(haskey( - primal_con_dual_var, + primal_con_to_dual_var_vec, MOI.ConstraintIndex{MOI.VariableIndex,MOI.GreaterThan{Float64}}(1), )) - @test primal_con_dual_var[MOI.ConstraintIndex{ + @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ MOI.VariableIndex, MOI.GreaterThan{Float64}, }( 2, )] == [MOI.VariableIndex(2)] - @test isempty(primal_dual_map.primal_var_dual_con) + @test isempty(primal_dual_map.primal_var_to_dual_con) - primal_parameter = primal_dual_map.primal_parameter - @test primal_parameter[MOI.VariableIndex(2)] == MOI.VariableIndex(3) + primal_parameter_to_dual_parameter = primal_dual_map.primal_parameter_to_dual_parameter + @test primal_parameter_to_dual_parameter[MOI.VariableIndex(2)] == MOI.VariableIndex(3) - primal_var_dual_quad_slack = primal_dual_map.primal_var_dual_quad_slack - @test primal_var_dual_quad_slack[MOI.VariableIndex(1)] == + primal_var_in_quad_obj_to_dual_slack_var = primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var + @test primal_var_in_quad_obj_to_dual_slack_var[MOI.VariableIndex(1)] == MOI.VariableIndex(4) end end diff --git a/test/Tests/test_structures.jl b/test/Tests/test_structures.jl index 13e07101..be1a244d 100644 --- a/test/Tests/test_structures.jl +++ b/test/Tests/test_structures.jl @@ -7,7 +7,7 @@ primal_dual_map = Dualization.PrimalDualMap{Float64}() @test Dualization.is_empty(primal_dual_map) push!( - primal_dual_map.primal_var_dual_con, + primal_dual_map.primal_var_to_dual_con, MOI.VariableIndex(1) => MOI.ConstraintIndex{MOI.VariableIndex,MOI.EqualTo}(1), ) diff --git a/test/optimize_abstract_models.jl b/test/optimize_abstract_models.jl index 2b7776ac..1c4b5e9b 100644 --- a/test/optimize_abstract_models.jl +++ b/test/optimize_abstract_models.jl @@ -10,7 +10,7 @@ solve it and retrieve the termination status and objective value function solve_abstract_model( model::MOI.ModelLike, optimizer_constructor, -) where {T} +) JuMP_model = JuMP.Model() MOI.copy_to(JuMP.backend(JuMP_model), model) set_optimizer(JuMP_model, optimizer_constructor) From 201121a6da33a91f944f4427e1b1eccaad8a9820 Mon Sep 17 00:00:00 2001 From: joaquimg Date: Mon, 14 Apr 2025 17:39:03 -0700 Subject: [PATCH 04/11] format --- src/MOI_wrapper.jl | 6 ++- src/constrained_variables.jl | 7 +++- src/dual_equality_constraints.jl | 49 ++++++++++++++++++----- src/dual_model_variables.jl | 15 +++++-- src/structures.jl | 24 ++++++++--- test/Tests/test_dualize_conic_linear.jl | 18 +++++---- test/Tests/test_dualize_quadratic.jl | 12 ++++-- test/Tests/test_partial_dual_linear.jl | 6 ++- test/Tests/test_partial_dual_quadratic.jl | 33 ++++++++++----- test/optimize_abstract_models.jl | 5 +-- 10 files changed, 124 insertions(+), 51 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 0586158c..870751c3 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -160,7 +160,8 @@ function MOI.modify( end vi = obj_change.variable if vi in keys(primal_dual_map.primal_convar_to_primal_convarcon_and_index) - ci_primal, index = 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 @@ -329,7 +330,8 @@ function MOI.get( ) primal_dual_map = optimizer.dual_problem.primal_dual_map if vi in keys(primal_dual_map.primal_convar_to_primal_convarcon_and_index) - ci_primal, idx = 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] return _get_at_index( optimizer, diff --git a/src/constrained_variables.jl b/src/constrained_variables.jl index e4517d83..e2aeb336 100644 --- a/src/constrained_variables.jl +++ b/src/constrained_variables.jl @@ -52,7 +52,9 @@ function _add_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) && !(vi in params), + vi -> + !haskey(m.primal_convar_to_primal_convarcon_and_index, vi) && + !(vi in params), f.variables, ) for (i, vi) in enumerate(f.variables) @@ -80,7 +82,8 @@ function _add_constrained_variable( 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) && !(f in params) + if !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 diff --git a/src/dual_equality_constraints.jl b/src/dual_equality_constraints.jl index 29d13a51..2718db38 100644 --- a/src/dual_equality_constraints.jl +++ b/src/dual_equality_constraints.jl @@ -80,7 +80,8 @@ function add_dual_equality_constraints( # Free variables for primal_vi in non_parameter_variables - if primal_vi in keys(primal_dual_map.primal_convar_to_primal_convarcon_and_index) + if primal_vi in + keys(primal_dual_map.primal_convar_to_primal_convarcon_and_index) continue # constrained variable end # Add equality constraint @@ -240,7 +241,10 @@ function add_scalar_affine_terms_from_quad_obj( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_var_in_quad_obj_to_dual_slack_var::Dict{MOI.VariableIndex,MOI.VariableIndex}, + primal_var_in_quad_obj_to_dual_slack_var::Dict{ + MOI.VariableIndex, + MOI.VariableIndex, + }, primal_objective::PrimalObjective{T}, sense_change::T, ) where {T} @@ -253,13 +257,15 @@ function add_scalar_affine_terms_from_quad_obj( dual_vi, ) else - dual_vi_1 = primal_var_in_quad_obj_to_dual_slack_var[term.variable_1] + dual_vi_1 = + primal_var_in_quad_obj_to_dual_slack_var[term.variable_1] push_to_scalar_affine_terms!( scalar_affine_terms[term.variable_2], -sense_change * MOI.coefficient(term), dual_vi_1, ) - dual_vi_2 = primal_var_in_quad_obj_to_dual_slack_var[term.variable_2] + dual_vi_2 = + primal_var_in_quad_obj_to_dual_slack_var[term.variable_2] push_to_scalar_affine_terms!( scalar_affine_terms[term.variable_1], -sense_change * MOI.coefficient(term), @@ -275,7 +281,10 @@ function add_scalar_affine_terms_from_quad_params( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_parameter_to_dual_parameter::Dict{MOI.VariableIndex,MOI.VariableIndex}, + primal_parameter_to_dual_parameter::Dict{ + MOI.VariableIndex, + MOI.VariableIndex, + }, primal_objective::PrimalObjective{T}, sense_change::T, ) where {T} @@ -324,7 +333,10 @@ function fill_scalar_affine_terms!( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_con_to_dual_var_vec::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}, + primal_con_to_dual_var_vec::Dict{ + MOI.ConstraintIndex, + Vector{MOI.VariableIndex}, + }, primal_model::MOI.ModelLike, ::Type{F}, ::Type{S}, @@ -342,7 +354,10 @@ end function get_scalar_affine_terms( primal_model::MOI.ModelLike, - primal_con_to_dual_var_vec::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}, + primal_con_to_dual_var_vec::Dict{ + MOI.ConstraintIndex, + Vector{MOI.VariableIndex}, + }, variables::Vector{MOI.VariableIndex}, con_types::Vector{Tuple{Type,Type}}, ::Type{T}, @@ -379,7 +394,10 @@ function fill_scalar_affine_terms!( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_con_to_dual_var_vec::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}, + primal_con_to_dual_var_vec::Dict{ + MOI.ConstraintIndex, + Vector{MOI.VariableIndex}, + }, primal_model::MOI.ModelLike, ci::MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},S}, ) where {T,S<:Union{MOI.GreaterThan{T},MOI.LessThan{T},MOI.EqualTo{T}}} @@ -400,7 +418,10 @@ function fill_scalar_affine_terms!( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_con_to_dual_var_vec::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}, + primal_con_to_dual_var_vec::Dict{ + MOI.ConstraintIndex, + Vector{MOI.VariableIndex}, + }, primal_model::MOI.ModelLike, ci::MOI.ConstraintIndex{MOI.VariableIndex,S}, ) where {T,S<:Union{MOI.GreaterThan{T},MOI.LessThan{T},MOI.EqualTo{T}}} @@ -426,7 +447,10 @@ function fill_scalar_affine_terms!( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_con_to_dual_var_vec::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}, + primal_con_to_dual_var_vec::Dict{ + MOI.ConstraintIndex, + Vector{MOI.VariableIndex}, + }, primal_model::MOI.ModelLike, ci::MOI.ConstraintIndex{MOI.VectorAffineFunction{T},S}, ) where {T,S<:MOI.AbstractVectorSet} @@ -450,7 +474,10 @@ function fill_scalar_affine_terms!( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_con_to_dual_var_vec::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}, + primal_con_to_dual_var_vec::Dict{ + MOI.ConstraintIndex, + Vector{MOI.VariableIndex}, + }, primal_model::MOI.ModelLike, ci::MOI.ConstraintIndex{MOI.VectorOfVariables,S}, ) where {T,S<:MOI.AbstractVectorSet} diff --git a/src/dual_model_variables.jl b/src/dual_model_variables.jl index 2437b3e5..01c54933 100644 --- a/src/dual_model_variables.jl +++ b/src/dual_model_variables.jl @@ -71,7 +71,10 @@ function push_to_primal_con_to_primal_constants_vec!( primal_con_to_primal_constants_vec::Dict{MOI.ConstraintIndex,Vector{T}}, ci::MOI.ConstraintIndex{F,S}, ) where {T,F<:MOI.AbstractScalarFunction,S<:MOI.AbstractScalarSet} - push!(primal_con_to_primal_constants_vec, ci => get_scalar_term(primal_model, ci)) + push!( + primal_con_to_primal_constants_vec, + ci => get_scalar_term(primal_model, ci), + ) return end @@ -136,7 +139,10 @@ function add_dual_variable( dual_model::MOI.ModelLike, primal_model::MOI.ModelLike, dual_names::DualNames, - primal_con_to_dual_var_vec::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}, + primal_con_to_dual_var_vec::Dict{ + MOI.ConstraintIndex, + Vector{MOI.VariableIndex}, + }, dual_obj_affine_terms::Dict{MOI.VariableIndex,T}, ci::MOI.ConstraintIndex{F,S}, ) where {T,F<:MOI.AbstractFunction,S<:MOI.AbstractSet} @@ -251,7 +257,10 @@ end # Save mapping between primal parameter and dual parameter function push_to_primal_parameter_to_dual_parameter!( - primal_parameter_to_dual_parameter::Dict{MOI.VariableIndex,MOI.VariableIndex}, + primal_parameter_to_dual_parameter::Dict{ + MOI.VariableIndex, + MOI.VariableIndex, + }, vi::MOI.VariableIndex, vi_dual::MOI.VariableIndex, ) diff --git a/src/structures.jl b/src/structures.jl index 4390295d..75444158 100644 --- a/src/structures.jl +++ b/src/structures.jl @@ -113,19 +113,31 @@ Main maps: Future name: primal_var_in_quad_obj_to_dual_slack_var """ mutable struct PrimalDualMap{T} - primal_convar_to_primal_convarcon_and_index::Dict{MOI.VariableIndex,Tuple{MOI.ConstraintIndex,Int}} + 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{MOI.ConstraintIndex,Vector{MOI.VariableIndex}} + primal_con_to_dual_var_vec::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_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} + primal_var_in_quad_obj_to_dual_slack_var::Dict{ + MOI.VariableIndex, + MOI.VariableIndex, + } function PrimalDualMap{T}() where {T} return new( @@ -179,7 +191,9 @@ function Base.getproperty(m::PrimalDualMap{T}, name::Symbol) where {T} end function is_empty(primal_dual_map::PrimalDualMap{T}) where {T} - return isempty(primal_dual_map.primal_convar_to_primal_convarcon_and_index) && + return 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) && diff --git a/test/Tests/test_dualize_conic_linear.jl b/test/Tests/test_dualize_conic_linear.jl index 141e3edd..e480e686 100644 --- a/test/Tests/test_dualize_conic_linear.jl +++ b/test/Tests/test_dualize_conic_linear.jl @@ -81,7 +81,8 @@ 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_convar_to_primal_convarcon_and_index[vi] == + (ci_nneg, i) end end @@ -234,11 +235,14 @@ vi = MOI.VariableIndex(i) @test !haskey(primal_var_to_dual_con, vi) 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) end end diff --git a/test/Tests/test_dualize_quadratic.jl b/test/Tests/test_dualize_quadratic.jl index 7228f451..ee6187ef 100644 --- a/test/Tests/test_dualize_quadratic.jl +++ b/test/Tests/test_dualize_quadratic.jl @@ -139,26 +139,30 @@ )] == [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)] == MOI.ConstraintIndex{ + @test primal_var_to_dual_con[MOI.VariableIndex(1)] == + MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 1, ) - @test primal_var_to_dual_con[MOI.VariableIndex(2)] == MOI.ConstraintIndex{ + @test primal_var_to_dual_con[MOI.VariableIndex(2)] == + MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 2, ) - @test primal_var_to_dual_con[MOI.VariableIndex(3)] == MOI.ConstraintIndex{ + @test primal_var_to_dual_con[MOI.VariableIndex(3)] == + MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 3, ) - primal_var_in_quad_obj_to_dual_slack_var = primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var + primal_var_in_quad_obj_to_dual_slack_var = + primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var @test primal_var_in_quad_obj_to_dual_slack_var[MOI.VariableIndex(1)] == MOI.VariableIndex(2 + 1) @test primal_var_in_quad_obj_to_dual_slack_var[MOI.VariableIndex(2)] == diff --git a/test/Tests/test_partial_dual_linear.jl b/test/Tests/test_partial_dual_linear.jl index 4451b759..b5302b13 100644 --- a/test/Tests/test_partial_dual_linear.jl +++ b/test/Tests/test_partial_dual_linear.jl @@ -90,7 +90,8 @@ )] == [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)] == MOI.ConstraintIndex{ + @test primal_var_to_dual_con[MOI.VariableIndex(1)] == + MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( @@ -286,7 +287,8 @@ )] == [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)] == MOI.ConstraintIndex{ + @test primal_var_to_dual_con[MOI.VariableIndex(2)] == + 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 60a27c26..6c47482c 100644 --- a/test/Tests/test_partial_dual_quadratic.jl +++ b/test/Tests/test_partial_dual_quadratic.jl @@ -120,23 +120,28 @@ )] == [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)] == MOI.ConstraintIndex{ + @test primal_var_to_dual_con[MOI.VariableIndex(1)] == + MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 1, ) - @test primal_var_to_dual_con[MOI.VariableIndex(2)] == MOI.ConstraintIndex{ + @test primal_var_to_dual_con[MOI.VariableIndex(2)] == + MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 2, ) - primal_parameter_to_dual_parameter = primal_dual_map.primal_parameter_to_dual_parameter - @test primal_parameter_to_dual_parameter[MOI.VariableIndex(3)] == MOI.VariableIndex(2 + 1) + primal_parameter_to_dual_parameter = + primal_dual_map.primal_parameter_to_dual_parameter + @test primal_parameter_to_dual_parameter[MOI.VariableIndex(3)] == + MOI.VariableIndex(2 + 1) - primal_var_in_quad_obj_to_dual_slack_var = primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var + primal_var_in_quad_obj_to_dual_slack_var = + primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var @test primal_var_in_quad_obj_to_dual_slack_var[MOI.VariableIndex(1)] == MOI.VariableIndex(2 + 1 + 1) @test primal_var_in_quad_obj_to_dual_slack_var[MOI.VariableIndex(2)] == @@ -239,10 +244,13 @@ primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con @test isempty(primal_var_to_dual_con) - primal_parameter_to_dual_parameter = primal_dual_map.primal_parameter_to_dual_parameter - @test primal_parameter_to_dual_parameter[MOI.VariableIndex(2)] == MOI.VariableIndex(3) + primal_parameter_to_dual_parameter = + primal_dual_map.primal_parameter_to_dual_parameter + @test primal_parameter_to_dual_parameter[MOI.VariableIndex(2)] == + MOI.VariableIndex(3) - primal_var_in_quad_obj_to_dual_slack_var = primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var + primal_var_in_quad_obj_to_dual_slack_var = + primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var @test primal_var_in_quad_obj_to_dual_slack_var[MOI.VariableIndex(1)] == MOI.VariableIndex(4) end @@ -342,10 +350,13 @@ @test isempty(primal_dual_map.primal_var_to_dual_con) - primal_parameter_to_dual_parameter = primal_dual_map.primal_parameter_to_dual_parameter - @test primal_parameter_to_dual_parameter[MOI.VariableIndex(2)] == MOI.VariableIndex(3) + primal_parameter_to_dual_parameter = + primal_dual_map.primal_parameter_to_dual_parameter + @test primal_parameter_to_dual_parameter[MOI.VariableIndex(2)] == + MOI.VariableIndex(3) - primal_var_in_quad_obj_to_dual_slack_var = primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var + primal_var_in_quad_obj_to_dual_slack_var = + primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var @test primal_var_in_quad_obj_to_dual_slack_var[MOI.VariableIndex(1)] == MOI.VariableIndex(4) end diff --git a/test/optimize_abstract_models.jl b/test/optimize_abstract_models.jl index 1c4b5e9b..04823c6b 100644 --- a/test/optimize_abstract_models.jl +++ b/test/optimize_abstract_models.jl @@ -7,10 +7,7 @@ Attach an MOI.ModelLike to an optimizer, solve it and retrieve the termination status and objective value """ -function solve_abstract_model( - model::MOI.ModelLike, - optimizer_constructor, -) +function solve_abstract_model(model::MOI.ModelLike, optimizer_constructor) JuMP_model = JuMP.Model() MOI.copy_to(JuMP.backend(JuMP_model), model) set_optimizer(JuMP_model, optimizer_constructor) From 91c35fc0888a09b57cd79451501f35dc51f62c85 Mon Sep 17 00:00:00 2001 From: joaquimg Date: Mon, 14 Apr 2025 17:47:14 -0700 Subject: [PATCH 05/11] format --- src/dual_equality_constraints.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/dual_equality_constraints.jl b/src/dual_equality_constraints.jl index e396601e..76b13a0b 100644 --- a/src/dual_equality_constraints.jl +++ b/src/dual_equality_constraints.jl @@ -80,7 +80,10 @@ 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 haskey( + primal_dual_map.primal_convar_to_primal_convarcon_and_index, + primal_vi, + ) continue # constrained variable end # Add equality constraint From 3b7b910a037aa9a28c282a082ed261fd4f8607cb Mon Sep 17 00:00:00 2001 From: joaquimg Date: Mon, 14 Apr 2025 19:18:34 -0700 Subject: [PATCH 06/11] more info --- src/dual_model_variables.jl | 2 +- src/structures.jl | 21 +++++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/dual_model_variables.jl b/src/dual_model_variables.jl index 01c54933..9fbc6c8c 100644 --- a/src/dual_model_variables.jl +++ b/src/dual_model_variables.jl @@ -13,7 +13,7 @@ function _add_dual_vars_in_dual_cones( ::Type{S}, ) 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 `in` check. + # 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) continue diff --git a/src/structures.jl b/src/structures.jl index 75444158..5b5c7710 100644 --- a/src/structures.jl +++ b/src/structures.jl @@ -75,27 +75,32 @@ Main maps: * `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. + 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. - note: possibly change this to + note: possibly change this to: + primal_con_to_dual_var_vec_and_convarcon - * `primal_con_to_dual_convarcon::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}`: maps - primal constraints to dual constrained variable. If the primal + * `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. + 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 constrains + that requires extra information in the case that the scalar set constains a constant (`EqualtTo`, `GreaterThan`, `LessThan`). - # TODO is the vec ever really used??? * `primal_parameter_to_dual_parameter::Dict{MOI.VariableIndex,MOI.VariableIndex}`: maps parameters in the primal to parameters in the dual model. From b94fe72a8bf930a0e062b92d5abb4029b286df24 Mon Sep 17 00:00:00 2001 From: joaquimg Date: Mon, 14 Apr 2025 19:41:43 -0700 Subject: [PATCH 07/11] cleanup dual cone --- src/add_dual_cone_constraint.jl | 32 ++++++++++++++++++++++++-------- src/dual_equality_constraints.jl | 12 ++++++------ src/dual_model_variables.jl | 6 +++--- src/utils.jl | 17 +++-------------- 4 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/add_dual_cone_constraint.jl b/src/add_dual_cone_constraint.jl index dd0f4ce7..64303beb 100644 --- a/src/add_dual_cone_constraint.jl +++ b/src/add_dual_cone_constraint.jl @@ -3,44 +3,60 @@ # Use of this source code is governed by an MIT-style license that can be found # in the LICENSE.md file or at https://opensource.org/licenses/MIT. -function add_dual_cone_constraint( +""" + _add_dual_cone_constraint(primal_model, dual_model, ci) + +Add variables to the dual models. These variables are associated with primal +constraints. If the primal constraint is of type +function-in-`MOI.Zeros` or function-in-`MOI.EqualTo(zero(T))`, then the dual +variable are unconstrained. Otherwise, the dual variables are created as +constrained variables in the dual model. + +The function returns a tuple: the first element is a vector dual variables, +and the second element is the constraint index of the dual constraint. If the +dual variable in not a constrained variable, the second element is `nothing`. +""" +function _add_dual_cone_constraint( dual_model::MOI.ModelLike, primal_model::MOI.ModelLike, ci::MOI.ConstraintIndex{F,S}, ) where {F<:MOI.AbstractScalarFunction,S<:MOI.AbstractScalarSet} vi, con_index = MOI.add_constrained_variable( dual_model, - _dual_set(get_set(primal_model, ci)), + _dual_set(MOI.get(primal_model, MOI.ConstraintSet(), ci)), ) return [vi], con_index end -function add_dual_cone_constraint( +function _add_dual_cone_constraint( dual_model::MOI.ModelLike, - primal_model::MOI.ModelLike, + ::MOI.ModelLike, ci::MOI.ConstraintIndex{F,MOI.EqualTo{T}}, ) where {T,F<:MOI.AbstractScalarFunction} vi = MOI.add_variable(dual_model) return [vi], nothing end -function add_dual_cone_constraint( +function _add_dual_cone_constraint( dual_model::MOI.ModelLike, primal_model::MOI.ModelLike, ci::MOI.ConstraintIndex{F,S}, ) where {F<:MOI.AbstractVectorFunction,S<:MOI.AbstractVectorSet} return MOI.add_constrained_variables( dual_model, - _dual_set(get_set(primal_model, ci)), + _dual_set(MOI.get(primal_model, MOI.ConstraintSet(), ci)), ) end -function add_dual_cone_constraint( +function _add_dual_cone_constraint( dual_model::MOI.ModelLike, primal_model::MOI.ModelLike, ci::MOI.ConstraintIndex{F,MOI.Zeros}, ) where {F<:MOI.AbstractVectorFunction} # Add as many variables as the dimension of the constraint - vis = MOI.add_variables(dual_model, get_ci_row_dimension(primal_model, ci)) + dim = MOI.output_dimension( + MOI.get(primal_model, MOI.ConstraintFunction(), ci), + ) + vis = MOI.add_variables(dual_model, dim) return vis, nothing end diff --git a/src/dual_equality_constraints.jl b/src/dual_equality_constraints.jl index 76b13a0b..47657984 100644 --- a/src/dual_equality_constraints.jl +++ b/src/dual_equality_constraints.jl @@ -403,7 +403,7 @@ function fill_scalar_affine_terms!( 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 = get_function(primal_model, ci) + 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 push_to_scalar_affine_terms!( @@ -434,7 +434,7 @@ function fill_scalar_affine_terms!( # the dual constraint associated to that primal variable. return end - moi_function = get_function(primal_model, ci) + moi_function = MOI.get(primal_model, MOI.ConstraintFunction(), ci) dual_vi = dual_var[1] # In this case we only have one vi push_to_scalar_affine_terms!( scalar_affine_terms[moi_function], @@ -456,8 +456,8 @@ function fill_scalar_affine_terms!( primal_model::MOI.ModelLike, ci::MOI.ConstraintIndex{MOI.VectorAffineFunction{T},S}, ) where {T,S<:MOI.AbstractVectorSet} - moi_function = get_function(primal_model, ci) - set = get_set(primal_model, ci) + 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] # term.output_index is the row of the MOI.VectorAffineFunction, @@ -490,8 +490,8 @@ function fill_scalar_affine_terms!( # the dual constraint associated to that primal variable. return end - moi_function = get_function(primal_model, ci) - set = get_set(primal_model, ci) + 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] push_to_scalar_affine_terms!( diff --git a/src/dual_model_variables.jl b/src/dual_model_variables.jl index 9fbc6c8c..3aaba02f 100644 --- a/src/dual_model_variables.jl +++ b/src/dual_model_variables.jl @@ -146,14 +146,14 @@ function add_dual_variable( dual_obj_affine_terms::Dict{MOI.VariableIndex,T}, ci::MOI.ConstraintIndex{F,S}, ) where {T,F<:MOI.AbstractFunction,S<:MOI.AbstractSet} - vis, con_index = add_dual_cone_constraint(dual_model, primal_model, ci) + vis, con_index = _add_dual_cone_constraint(dual_model, primal_model, ci) # Add the map of the added dual variable to the relationated constraint push!(primal_con_to_dual_var_vec, ci => vis) # Get constraint name ci_name = MOI.get(primal_model, MOI.ConstraintName(), ci) # Add each vi to the dictionary - func = get_function(primal_model, ci) - set = get_set(primal_model, ci) + func = MOI.get(primal_model, MOI.ConstraintFunction(), ci) + set = MOI.get(primal_model, MOI.ConstraintSet(), ci) for (i, vi) in enumerate(vis) push_to_dual_obj_aff_terms!( primal_model, diff --git a/src/utils.jl b/src/utils.jl index 6a7b9327..d9a4ea8e 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -4,23 +4,12 @@ # in the LICENSE.md file or at https://opensource.org/licenses/MIT. # Some useful wrappers -function get_function(model::MOI.ModelLike, ci::MOI.ConstraintIndex) - return MOI.get(model, MOI.ConstraintFunction(), ci) -end - -function get_set(model::MOI.ModelLike, ci::MOI.ConstraintIndex) - return MOI.get(model, MOI.ConstraintSet(), ci) -end - -function get_ci_row_dimension(model::MOI.ModelLike, ci::MOI.ConstraintIndex) - return MOI.output_dimension(get_function(model, ci)) -end function get_scalar_term( model::MOI.ModelLike, ci::MOI.ConstraintIndex{MOI.VariableIndex,S}, ) where {S<:MOI.AbstractScalarSet} - return [-MOI.constant(get_set(model, ci))] + return [-MOI.constant(MOI.get(model, MOI.ConstraintSet(), ci))] end function get_scalar_term( @@ -28,8 +17,8 @@ function get_scalar_term( ci::MOI.ConstraintIndex{F,S}, ) where {F<:MOI.AbstractScalarFunction,S<:MOI.AbstractScalarSet} return [ - MOI.constant(get_function(model, ci)) - - MOI.constant(get_set(model, ci)), + MOI.constant(MOI.get(model, MOI.ConstraintFunction(), ci)) - + MOI.constant(MOI.get(model, MOI.ConstraintSet(), ci)), ] end From ebd8c45202242d1f32a33145ca5f3f8c380fe9ff Mon Sep 17 00:00:00 2001 From: joaquimg Date: Mon, 14 Apr 2025 19:44:22 -0700 Subject: [PATCH 08/11] con variables --- src/constrained_variables.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/constrained_variables.jl b/src/constrained_variables.jl index e2aeb336..964af02f 100644 --- a/src/constrained_variables.jl +++ b/src/constrained_variables.jl @@ -49,12 +49,12 @@ function _add_constrained_variables( ) for ci in cis f = MOI.get(primal_model, MOI.ConstraintFunction(), ci) + # try to add variables as 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) && - !(vi in params), + !(vi in params) && + vi -> !haskey(m.primal_convar_to_primal_convarcon_and_index, vi), f.variables, ) for (i, vi) in enumerate(f.variables) From 3de2146e473e9891318e1f066879fe5abfd60bdd Mon Sep 17 00:00:00 2001 From: joaquimg Date: Mon, 14 Apr 2025 20:10:39 -0700 Subject: [PATCH 09/11] fix names --- src/constrained_variables.jl | 7 +-- src/dual_equality_constraints.jl | 82 ++++++++++++++++---------------- src/dualize.jl | 4 +- test/Tests/test_dot.jl | 10 ++-- 4 files changed, 53 insertions(+), 50 deletions(-) diff --git a/src/constrained_variables.jl b/src/constrained_variables.jl index 964af02f..21128a27 100644 --- a/src/constrained_variables.jl +++ b/src/constrained_variables.jl @@ -3,7 +3,7 @@ # Use of this source code is governed by an MIT-style license that can be found # in the LICENSE.md file or at https://opensource.org/licenses/MIT. -function add_constrained_variables( +function _add_all_constrained_variables( dual_problem, primal_model, variable_parameters, @@ -53,8 +53,9 @@ function _add_constrained_variables( if all( # no element of the VectorOfVariables is a constrained variable # and not a parameter - !(vi in params) && - vi -> !haskey(m.primal_convar_to_primal_convarcon_and_index, vi), + vi -> + !haskey(m.primal_convar_to_primal_convarcon_and_index, vi) && + !(vi in params), f.variables, ) for (i, vi) in enumerate(f.variables) diff --git a/src/dual_equality_constraints.jl b/src/dual_equality_constraints.jl index 47657984..801a8e19 100644 --- a/src/dual_equality_constraints.jl +++ b/src/dual_equality_constraints.jl @@ -3,7 +3,7 @@ # Use of this source code is governed by an MIT-style license that can be found # in the LICENSE.md file or at https://opensource.org/licenses/MIT. -function add_dual_equality_constraints( +function _add_dual_equality_constraints( dual_model::MOI.ModelLike, primal_model::MOI.ModelLike, primal_dual_map::PrimalDualMap, @@ -22,9 +22,10 @@ function add_dual_equality_constraints( non_parameter_variables = setdiff(all_variables, variable_parameters) # Loop at every constraint to collect the scalar affine terms in the - # `scalar_affine_terms` list. + # `scalar_affine_terms` list (a dics mapping variable index to + # a scalar affine function). # TODO: flip these signs a priorie instead of require post processing later - scalar_affine_terms = get_scalar_affine_terms( + scalar_affine_terms = _get_scalar_affine_terms( primal_model, primal_dual_map.primal_con_to_dual_var_vec, all_variables, @@ -33,14 +34,14 @@ function add_dual_equality_constraints( ) # get constants (rhs) of dual constraints from primal objective coefficients - scalar_terms = get_scalar_terms(primal_objective) + scalar_terms = _get_scalar_terms(primal_objective) # Collect affine terms of dual constraints that come from the quadratic # part of the primal objective function, and add them into # `scalar_affine_terms`. # These terms are added with flipped signs (because the sign will be flipped again). # TODO: unflip these signs - add_scalar_affine_terms_from_quad_obj( + _add_scalar_affine_terms_from_quad_obj( scalar_affine_terms, primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var, primal_objective, @@ -50,7 +51,7 @@ function add_dual_equality_constraints( # terms from mixing variables and parameters # These terms are added with flipped signs (because the sign will be flipped again). # TODO: unflip these signs - add_scalar_affine_terms_from_quad_params( + _add_scalar_affine_terms_from_quad_params( scalar_affine_terms, primal_dual_map.primal_parameter_to_dual_parameter, primal_objective, @@ -103,7 +104,7 @@ function add_dual_equality_constraints( ) # Set constraint name with the name of the associated priaml variable if !is_empty(dual_names) - set_dual_constraint_name( + _set_dual_constraint_name( dual_model, primal_model, primal_vi, @@ -112,7 +113,7 @@ function add_dual_equality_constraints( ) end # Add primal variable to dual contraint to the link dictionary - push!(primal_dual_map.primal_var_to_dual_con, primal_vi => dual_ci) + primal_dual_map.primal_var_to_dual_con[primal_vi] = dual_ci end return scalar_affine_terms end @@ -227,7 +228,7 @@ function _add_constrained_variable_constraint( set_dual, ) if !is_empty(dual_names) - set_dual_constraint_name( + _set_dual_constraint_name( dual_model, primal_model, primal_vi, @@ -238,7 +239,7 @@ function _add_constrained_variable_constraint( return end -function add_scalar_affine_terms_from_quad_obj( +function _add_scalar_affine_terms_from_quad_obj( scalar_affine_terms::Dict{ MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, @@ -253,7 +254,7 @@ function add_scalar_affine_terms_from_quad_obj( for term in primal_objective.obj.quadratic_terms if term.variable_1 == term.variable_2 dual_vi = primal_var_in_quad_obj_to_dual_slack_var[term.variable_1] - push_to_scalar_affine_terms!( + _push_to_scalar_affine_terms!( scalar_affine_terms[term.variable_1], -sense_change * MOI.coefficient(term), dual_vi, @@ -261,14 +262,14 @@ function add_scalar_affine_terms_from_quad_obj( else dual_vi_1 = primal_var_in_quad_obj_to_dual_slack_var[term.variable_1] - push_to_scalar_affine_terms!( + _push_to_scalar_affine_terms!( scalar_affine_terms[term.variable_2], -sense_change * MOI.coefficient(term), dual_vi_1, ) dual_vi_2 = primal_var_in_quad_obj_to_dual_slack_var[term.variable_2] - push_to_scalar_affine_terms!( + _push_to_scalar_affine_terms!( scalar_affine_terms[term.variable_1], -sense_change * MOI.coefficient(term), dual_vi_2, @@ -278,7 +279,7 @@ function add_scalar_affine_terms_from_quad_obj( return end -function add_scalar_affine_terms_from_quad_params( +function _add_scalar_affine_terms_from_quad_params( scalar_affine_terms::Dict{ MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, @@ -293,7 +294,7 @@ function add_scalar_affine_terms_from_quad_params( for (key, val) in primal_objective.quad_cross_parameters for term in val dual_vi = primal_parameter_to_dual_parameter[term.variable] - push_to_scalar_affine_terms!( + _push_to_scalar_affine_terms!( scalar_affine_terms[key], -sense_change * MOI.coefficient(term), dual_vi, @@ -302,7 +303,7 @@ function add_scalar_affine_terms_from_quad_params( end end -function set_dual_constraint_name( +function _set_dual_constraint_name( dual_model::MOI.ModelLike, primal_model::MOI.ModelLike, primal_vi::MOI.VariableIndex, @@ -318,7 +319,7 @@ function set_dual_constraint_name( return end -function get_scalar_terms(primal_objective::PrimalObjective{T}) where {T} +function _get_scalar_terms(primal_objective::PrimalObjective{T}) where {T} scalar_terms = Dict{MOI.VariableIndex,T}() for term in get_affine_terms(primal_objective) if haskey(scalar_terms, term.variable) @@ -330,7 +331,8 @@ function get_scalar_terms(primal_objective::PrimalObjective{T}) where {T} return scalar_terms end -function fill_scalar_affine_terms!( +# function barrier +function _fill_scalar_affine_terms!( scalar_affine_terms::Dict{ MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, @@ -344,7 +346,7 @@ function fill_scalar_affine_terms!( ::Type{S}, ) where {T,F,S} for ci in MOI.get(primal_model, MOI.ListOfConstraintIndices{F,S}()) - fill_scalar_affine_terms!( + _fill_scalar_affine_terms!( scalar_affine_terms, primal_con_to_dual_var_vec, primal_model, @@ -354,7 +356,7 @@ function fill_scalar_affine_terms!( return end -function get_scalar_affine_terms( +function _get_scalar_affine_terms( primal_model::MOI.ModelLike, primal_con_to_dual_var_vec::Dict{ MOI.ConstraintIndex, @@ -369,7 +371,7 @@ function get_scalar_affine_terms( vi => MOI.ScalarAffineTerm{T}[] for vi in variables ) for (F, S) in con_types - fill_scalar_affine_terms!( + _fill_scalar_affine_terms!( scalar_affine_terms, primal_con_to_dual_var_vec, primal_model, @@ -380,7 +382,7 @@ function get_scalar_affine_terms( return scalar_affine_terms end -function push_to_scalar_affine_terms!( +function _push_to_scalar_affine_terms!( scalar_affine_terms::Vector{MOI.ScalarAffineTerm{T}}, affine_term::T, vi::MOI.VariableIndex, @@ -391,7 +393,7 @@ function push_to_scalar_affine_terms!( return end -function fill_scalar_affine_terms!( +function _fill_scalar_affine_terms!( scalar_affine_terms::Dict{ MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, @@ -406,7 +408,7 @@ function fill_scalar_affine_terms!( 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 - push_to_scalar_affine_terms!( + _push_to_scalar_affine_terms!( scalar_affine_terms[term.variable], MOI.coefficient(term), dual_vi, @@ -415,7 +417,7 @@ function fill_scalar_affine_terms!( return end -function fill_scalar_affine_terms!( +function _fill_scalar_affine_terms!( scalar_affine_terms::Dict{ MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, @@ -436,7 +438,7 @@ function fill_scalar_affine_terms!( end moi_function = MOI.get(primal_model, MOI.ConstraintFunction(), ci) dual_vi = dual_var[1] # In this case we only have one vi - push_to_scalar_affine_terms!( + _push_to_scalar_affine_terms!( scalar_affine_terms[moi_function], one(T), dual_vi, @@ -444,7 +446,7 @@ function fill_scalar_affine_terms!( return end -function fill_scalar_affine_terms!( +function _fill_scalar_affine_terms!( scalar_affine_terms::Dict{ MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, @@ -462,7 +464,7 @@ function fill_scalar_affine_terms!( dual_vi = primal_con_to_dual_var_vec[ci][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!( + _push_to_scalar_affine_terms!( scalar_affine_terms[term.scalar_term.variable], set_dot(term.output_index, set, T) * MOI.coefficient(term), dual_vi, @@ -471,7 +473,7 @@ function fill_scalar_affine_terms!( return end -function fill_scalar_affine_terms!( +function _fill_scalar_affine_terms!( scalar_affine_terms::Dict{ MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, @@ -494,7 +496,7 @@ function fill_scalar_affine_terms!( set = MOI.get(primal_model, MOI.ConstraintSet(), ci) for (i, variable) in enumerate(moi_function.variables) dual_vi = dual_vars[i] - push_to_scalar_affine_terms!( + _push_to_scalar_affine_terms!( scalar_affine_terms[variable], set_dot(i, set, T) * one(T), dual_vi, @@ -503,33 +505,33 @@ function fill_scalar_affine_terms!( return end -struct CanonicalVector{T} <: AbstractVector{T} +struct _CanonicalVector{T} <: AbstractVector{T} index::Int n::Int end -Base.eltype(::Type{CanonicalVector{T}}) where {T} = T +Base.eltype(::Type{_CanonicalVector{T}}) where {T} = T -Base.length(v::CanonicalVector) = v.n +Base.length(v::_CanonicalVector) = v.n -Base.size(v::CanonicalVector) = (v.n,) +Base.size(v::_CanonicalVector) = (v.n,) -function Base.getindex(v::CanonicalVector{T}, i::Integer) where {T} +function Base.getindex(v::_CanonicalVector{T}, i::Integer) where {T} return convert(T, i == v.index) end # This is much faster than the default implementation that goes # through all entries even if only one is nonzero. function LinearAlgebra.dot( - x::CanonicalVector{T}, - y::CanonicalVector{T}, + x::_CanonicalVector{T}, + y::_CanonicalVector{T}, ) where {T} return convert(T, x.index == y.index) end function MOI.Utilities.triangle_dot( - x::CanonicalVector{T}, - y::CanonicalVector{T}, + x::_CanonicalVector{T}, + y::_CanonicalVector{T}, dim::Int, offset::Int, ) where {T} @@ -543,7 +545,7 @@ function MOI.Utilities.triangle_dot( end function set_dot(i::Integer, s::MOI.AbstractVectorSet, T::Type) - vec = CanonicalVector{T}(i, MOI.dimension(s)) + vec = _CanonicalVector{T}(i, MOI.dimension(s)) return MOI.Utilities.set_dot(vec, vec, s) end diff --git a/src/dualize.jl b/src/dualize.jl index 467048e8..4f6915cf 100644 --- a/src/dualize.jl +++ b/src/dualize.jl @@ -97,7 +97,7 @@ function dualize( # the respective primal variable will not be a constrained variable (with # respect to that constraint). if consider_constrained_variables - add_constrained_variables( + _add_all_constrained_variables( dual_problem, primal_model, variable_parameters, @@ -167,7 +167,7 @@ function dualize( # returns `scalar_affine_terms` # because the terms associated to variables that are parameters will be used # in `get_dual_objective` - scalar_affine_terms = add_dual_equality_constraints( + scalar_affine_terms = _add_dual_equality_constraints( dual_problem.dual_model, primal_model, dual_problem.primal_dual_map, diff --git a/test/Tests/test_dot.jl b/test/Tests/test_dot.jl index 9118fbc0..f795870a 100644 --- a/test/Tests/test_dot.jl +++ b/test/Tests/test_dot.jl @@ -4,17 +4,17 @@ # in the LICENSE.md file or at https://opensource.org/licenses/MIT. @testset "test_dot.jl" begin - a = Dualization.CanonicalVector{Int}(1, 3) + a = Dualization._CanonicalVector{Int}(1, 3) @test collect(a) == [1, 0, 0] - b = Dualization.CanonicalVector{Int}(2, 3) + b = Dualization._CanonicalVector{Int}(2, 3) @test collect(b) == [0, 1, 0] set = MOI.PositiveSemidefiniteConeTriangle(2) @test MOI.Utilities.set_dot(a, b, set) == 0 @test MOI.Utilities.set_dot(a, a, set) == 1 @test MOI.Utilities.set_dot(b, b, set) == 2 - a = Dualization.CanonicalVector{Int}(1, 4) - b = Dualization.CanonicalVector{Int}(2, 4) - c = Dualization.CanonicalVector{Int}(3, 4) + a = Dualization._CanonicalVector{Int}(1, 4) + b = Dualization._CanonicalVector{Int}(2, 4) + c = Dualization._CanonicalVector{Int}(3, 4) set = MOI.RootDetConeTriangle(2) @test MOI.Utilities.set_dot(a, b, set) == 0 @test MOI.Utilities.set_dot(b, c, set) == 0 From 3d316dd3879e2ca0e82c1bb569b627eb2ef7439a Mon Sep 17 00:00:00 2001 From: joaquimg Date: Tue, 15 Apr 2025 01:14:57 -0700 Subject: [PATCH 10/11] more cleanup --- src/MOI_wrapper.jl | 178 ++++++------------ src/constrained_variables.jl | 27 +-- src/dual_equality_constraints.jl | 14 +- src/dual_model_variables.jl | 215 +++++----------------- src/dualize.jl | 18 +- src/objective_coefficients.jl | 77 +++----- src/supported.jl | 24 ++- src/utils.jl | 10 +- test/Tests/test_MOI_wrapper.jl | 10 +- test/Tests/test_dual_model_variables.jl | 38 ---- test/Tests/test_objective_coefficients.jl | 28 +-- test/Tests/test_supported.jl | 6 +- 12 files changed, 198 insertions(+), 447 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 463eb6d9..94a3765d 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -88,7 +88,7 @@ function MOI.supports( else MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}() end - return supported_obj(F) && + return supported_objective(F) && MOI.supports(optimizer.dual_problem.dual_model, attr) end @@ -189,7 +189,8 @@ function MOI.supports_add_constrained_variables( MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}, ) - # If `_dual_set_type(MOI.Reals)` was `MOI.Zeros`, we would not need this method as special case of the one below + # If `_dual_set_type(MOI.Reals)` was `MOI.Zeros`, + # we would not need this method as special case of the one below end function MOI.supports_add_constrained_variables( @@ -236,122 +237,48 @@ function MOI.empty!(optimizer::DualOptimizer) return end -# MOI.get auxiliary functions -function get_ci_dual_problem(optimizer::DualOptimizer, vi::MOI.VariableIndex) - return optimizer.dual_problem.primal_dual_map.primal_var_to_dual_con[vi] -end - -function get_ci_dual_problem(optimizer::DualOptimizer, ci::MOI.ConstraintIndex) - return optimizer.dual_problem.primal_dual_map.primal_con_to_dual_convarcon[ci] -end - -function get_primal_ci_constant( - optimizer::DualOptimizer, - ci::MOI.ConstraintIndex, -) - return first(get_primal_ci_constants(optimizer, ci)) -end - -function get_primal_ci_constants( - optimizer::DualOptimizer, - ci::MOI.ConstraintIndex, -) - return optimizer.dual_problem.primal_dual_map.primal_con_to_primal_constants_vec[ci] -end - -function get_vi_dual_problem(optimizer::DualOptimizer, ci::MOI.ConstraintIndex) - return first(get_vis_dual_problem(optimizer, ci)) -end - -function get_vis_dual_problem(optimizer::DualOptimizer, ci::MOI.ConstraintIndex) - return optimizer.dual_problem.primal_dual_map.primal_con_to_dual_var_vec[ci] -end - function MOI.get(optimizer::DualOptimizer, ::MOI.SolverName) name = MOI.get(optimizer.dual_problem.dual_model, MOI.SolverName()) return "Dual model with $name attached" end -function _get( - ::DualOptimizer{T}, - ::MOI.AbstractConstraintAttribute, - ::MOI.ConstraintIndex{MOI.VariableIndex,MOI.EqualTo{T}}, - ::MOI.ConstraintIndex{Nothing,Nothing}, -) where {T} - return zero(T) -end - -function _get( - optimizer::DualOptimizer, - attr::MOI.AbstractConstraintAttribute, - ::MOI.ConstraintIndex, - ci::MOI.ConstraintIndex, -) - return MOI.get(optimizer.dual_problem.dual_model, attr, ci) -end - -function _get( - optimizer::DualOptimizer{T}, - ::MOI.AbstractConstraintAttribute, - ci_primal::MOI.ConstraintIndex{MOI.VectorOfVariables,MOI.Zeros}, - ::MOI.ConstraintIndex{Nothing,Nothing}, -) where {T} - n = MOI.output_dimension( - optimizer.dual_problem.primal_dual_map.primal_convarcon_to_dual_function[ci_primal], - ) - return zeros(T, n) -end - -function _get_at_index( - optimizer::DualOptimizer, - attr::MOI.AbstractConstraintAttribute, - ci_primal::MOI.ConstraintIndex{MOI.VariableIndex}, - ci_dual::MOI.ConstraintIndex, - idx, -) - @assert isone(idx) - return _get(optimizer, attr, ci_primal, ci_dual) -end - -function _get_at_index( - optimizer::DualOptimizer, - attr::MOI.AbstractConstraintAttribute, - ci_primal::MOI.ConstraintIndex{MOI.VectorOfVariables}, - ci_dual::MOI.ConstraintIndex, - idx, -) - return _get(optimizer, attr, ci_primal, ci_dual)[idx] -end - function MOI.get( - optimizer::DualOptimizer, + optimizer::DualOptimizer{T}, ::MOI.VariablePrimal, vi::MOI.VariableIndex, -) +) 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] - return _get_at_index( - optimizer, - MOI.ConstraintDual(), - ci_primal, - ci_dual, - idx, - ) + 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]::Number + else + return MOI.get( + optimizer.dual_problem.dual_model, + MOI.ConstraintDual(), + ci_dual, + )::Number + end else return -MOI.get( optimizer.dual_problem.dual_model, MOI.ConstraintDual(), - get_ci_dual_problem(optimizer, vi), - ) + primal_dual_map.primal_var_to_dual_con[vi], + )::Number end end function MOI.get( optimizer::DualOptimizer, - attr::MOI.ConstraintDual, + ::MOI.ConstraintDual, ci::MOI.ConstraintIndex{F,S}, ) where {F<:MOI.AbstractScalarFunction,S<:MOI.AbstractScalarSet} primal_dual_map = optimizer.dual_problem.primal_dual_map @@ -382,8 +309,8 @@ function MOI.get( return MOI.get( optimizer.dual_problem.dual_model, MOI.VariablePrimal(), - get_vi_dual_problem(optimizer, ci), - ) + primal_dual_map.primal_con_to_dual_var_vec[ci][], + )::Number end end @@ -415,31 +342,36 @@ function MOI.get( return MOI.get.( optimizer.dual_problem.dual_model, MOI.VariablePrimal(), - get_vis_dual_problem(optimizer, ci), + primal_dual_map.primal_con_to_dual_var_vec[ci], ) end end function MOI.get( - optimizer::DualOptimizer, + optimizer::DualOptimizer{T}, ::MOI.ConstraintPrimal, ci::MOI.ConstraintIndex{F,S}, -) where {F<:MOI.AbstractScalarFunction,S<:MOI.AbstractScalarSet} +) 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) - return _get( - optimizer, - MOI.ConstraintDual(), - ci, - 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 zero(T) + else + return MOI.get( + optimizer.dual_problem.dual_model, + MOI.ConstraintDual(), + ci_dual, + )::Number + end else - primal_ci_constant = get_primal_ci_constant(optimizer, ci) + primal_ci_constant = + primal_dual_map.primal_con_to_primal_constants_vec[ci][1] # If it has no key then there is no dual constraint if !haskey(primal_dual_map.primal_con_to_dual_convarcon, ci) return -primal_ci_constant end - ci_dual_problem = get_ci_dual_problem(optimizer, ci) + ci_dual_problem = primal_dual_map.primal_con_to_dual_convarcon[ci] return MOI.get( optimizer.dual_problem.dual_model, MOI.ConstraintDual(), @@ -455,20 +387,28 @@ function MOI.get( ) 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) - return _get( - optimizer, - MOI.ConstraintDual(), - ci, - 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) + else + return MOI.get( + optimizer.dual_problem.dual_model, + MOI.ConstraintDual(), + ci_dual, + ) + end else # If it has no key then there is no dual constraint if !haskey(primal_dual_map.primal_con_to_dual_convarcon, ci) # The number of dual variable associated with the primal constraint is the ci dimension - ci_dimension = length(get_vis_dual_problem(optimizer, ci)) + ci_dimension = + length(primal_dual_map.primal_con_to_dual_var_vec[ci]) return zeros(T, ci_dimension) end - ci_dual_problem = get_ci_dual_problem(optimizer, ci) + ci_dual_problem = primal_dual_map.primal_con_to_dual_convarcon[ci] return MOI.get( optimizer.dual_problem.dual_model, MOI.ConstraintDual(), @@ -478,12 +418,12 @@ function MOI.get( end function MOI.get(optimizer::DualOptimizer, ::MOI.TerminationStatus) - return dual_status( + return _dual_status( MOI.get(optimizer.dual_problem.dual_model, MOI.TerminationStatus()), ) end -function dual_status(term::MOI.TerminationStatusCode) +function _dual_status(term::MOI.TerminationStatusCode) if term == MOI.INFEASIBLE return MOI.DUAL_INFEASIBLE elseif term == MOI.DUAL_INFEASIBLE diff --git a/src/constrained_variables.jl b/src/constrained_variables.jl index 21128a27..7e86cf15 100644 --- a/src/constrained_variables.jl +++ b/src/constrained_variables.jl @@ -3,7 +3,7 @@ # Use of this source code is governed by an MIT-style license that can be found # in the LICENSE.md file or at https://opensource.org/licenses/MIT. -function _add_all_constrained_variables( +function _select_constrained_variables( dual_problem, primal_model, variable_parameters, @@ -15,21 +15,12 @@ function _add_all_constrained_variables( ) params = Set(variable_parameters) for S in single_or_vector_variables_types - if S <: MOI.AbstractVectorSet - _add_constrained_variables( - dual_problem.primal_dual_map, - primal_model, - S, - params, - ) - elseif S <: MOI.AbstractScalarSet - _add_constrained_variable( - dual_problem.primal_dual_map, - primal_model, - S, - params, - ) - end + _select_constrained_variables( + dual_problem.primal_dual_map, + primal_model, + S, + params, + ) end return end @@ -37,7 +28,7 @@ end const NO_CONSTRAINT = MOI.ConstraintIndex{Nothing,Nothing}(0) # Function barrier for the type instability of `F` and `S`. -function _add_constrained_variables( +function _select_constrained_variables( m::PrimalDualMap, primal_model, ::Type{S}, @@ -69,7 +60,7 @@ function _add_constrained_variables( return end -function _add_constrained_variable( +function _select_constrained_variables( m::PrimalDualMap, primal_model, ::Type{S}, diff --git a/src/dual_equality_constraints.jl b/src/dual_equality_constraints.jl index 801a8e19..e6e5d133 100644 --- a/src/dual_equality_constraints.jl +++ b/src/dual_equality_constraints.jl @@ -8,7 +8,7 @@ function _add_dual_equality_constraints( primal_model::MOI.ModelLike, primal_dual_map::PrimalDualMap, dual_names::DualNames, - primal_objective::PrimalObjective{T}, + primal_objective::_PrimalObjective{T}, con_types::Vector{Tuple{Type,Type}}, variable_parameters::Vector{MOI.VariableIndex}, ) where {T} @@ -34,7 +34,7 @@ function _add_dual_equality_constraints( ) # get constants (rhs) of dual constraints from primal objective coefficients - scalar_terms = _get_scalar_terms(primal_objective) + scalar_terms = _get_dual_constraint_constants(primal_objective) # Collect affine terms of dual constraints that come from the quadratic # part of the primal objective function, and add them into @@ -248,7 +248,7 @@ function _add_scalar_affine_terms_from_quad_obj( MOI.VariableIndex, MOI.VariableIndex, }, - primal_objective::PrimalObjective{T}, + primal_objective::_PrimalObjective{T}, sense_change::T, ) where {T} for term in primal_objective.obj.quadratic_terms @@ -288,7 +288,7 @@ function _add_scalar_affine_terms_from_quad_params( MOI.VariableIndex, MOI.VariableIndex, }, - primal_objective::PrimalObjective{T}, + primal_objective::_PrimalObjective{T}, sense_change::T, ) where {T} for (key, val) in primal_objective.quad_cross_parameters @@ -319,9 +319,11 @@ function _set_dual_constraint_name( return end -function _get_scalar_terms(primal_objective::PrimalObjective{T}) where {T} +function _get_dual_constraint_constants( + primal_objective::_PrimalObjective{T}, +) where {T} scalar_terms = Dict{MOI.VariableIndex,T}() - for term in get_affine_terms(primal_objective) + for term in primal_objective.obj.affine_terms if haskey(scalar_terms, term.variable) scalar_terms[term.variable] += MOI.coefficient(term) else diff --git a/src/dual_model_variables.jl b/src/dual_model_variables.jl index 3aaba02f..e29f45d0 100644 --- a/src/dual_model_variables.jl +++ b/src/dual_model_variables.jl @@ -16,12 +16,15 @@ function _add_dual_vars_in_dual_cones( # 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) + # primal constraints that are the main constraints of + # constrained variables have no dual variable associated + # bacause they associated with dual constraints continue end # Add dual variable to dual cone # Fill a dual objective dictionary # Fill the primal_con_to_dual_var_vec dictionary - ci_dual = add_dual_variable( + ci_dual = _add_dual_variable( dual_model, primal_model, dual_names, @@ -29,12 +32,10 @@ function _add_dual_vars_in_dual_cones( dual_obj_affine_terms, ci, ) - push_to_primal_con_to_dual_convarcon!( - primal_dual_map.primal_con_to_dual_convarcon, - ci, - ci_dual, - ) - push_to_primal_con_to_primal_constants_vec!( + if ci_dual !== nothing + primal_dual_map.primal_con_to_dual_convarcon[ci] = ci_dual + end + _cache_primal_constraint_constant!( primal_model, primal_dual_map.primal_con_to_primal_constants_vec, ci, @@ -43,7 +44,8 @@ function _add_dual_vars_in_dual_cones( return end -function add_dual_vars_in_dual_cones( +# barrier function +function _add_dual_vars_in_dual_cones( dual_model::MOI.ModelLike, primal_model::MOI.ModelLike, primal_dual_map::PrimalDualMap{T}, @@ -66,19 +68,17 @@ function add_dual_vars_in_dual_cones( end # Utils for primal_con_to_primal_constants_vec dict -function push_to_primal_con_to_primal_constants_vec!( +function _cache_primal_constraint_constant!( primal_model::MOI.ModelLike, primal_con_to_primal_constants_vec::Dict{MOI.ConstraintIndex,Vector{T}}, ci::MOI.ConstraintIndex{F,S}, ) where {T,F<:MOI.AbstractScalarFunction,S<:MOI.AbstractScalarSet} - push!( - primal_con_to_primal_constants_vec, - ci => get_scalar_term(primal_model, ci), - ) + primal_con_to_primal_constants_vec[ci] = + _get_normalized_constant(primal_model, ci) return end -function push_to_primal_con_to_primal_constants_vec!( +function _cache_primal_constraint_constant!( primal_model::MOI.ModelLike, primal_con_to_primal_constants_vec::Dict{MOI.ConstraintIndex,Vector{T}}, ci::MOI.ConstraintIndex{F,S}, @@ -90,52 +90,7 @@ function push_to_primal_con_to_primal_constants_vec!( return end -# Utils for primal_con_to_dual_convarcon dict -function push_to_primal_con_to_dual_convarcon!( - primal_con_to_dual_convarcon::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}, - ci::MOI.ConstraintIndex, - ci_dual::MOI.ConstraintIndex, -) - push!(primal_con_to_dual_convarcon, ci => ci_dual) - return -end - -function push_to_primal_con_to_dual_convarcon!( - primal_con_to_dual_convarcon::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}, - ci::MOI.ConstraintIndex, - ci_dual::Nothing, -) - return # Don't put in the dict a nothing value -end - -# Utils for dual_obj_affine_terms dict -function push_to_dual_obj_aff_terms!( - primal_model::MOI.ModelLike, - dual_obj_affine_terms::Dict{MOI.VariableIndex,T}, - vi::MOI.VariableIndex, - func::MOI.AbstractFunction, - set::MOI.AbstractSet, - i::Int, -) where {T} - value = set_dot(i, set, T) * get_scalar_term(func, set, i) - if !iszero(value) # If value is different than 0 add to the dictionary - push!(dual_obj_affine_terms, vi => value) - end - return -end - -function push_to_dual_obj_aff_terms!( - ::MOI.ModelLike, - ::Dict{MOI.VariableIndex}, - ::MOI.VariableIndex, - ::MOI.VectorOfVariables, - ::MOI.AbstractVectorSet, - i::Int, -) - return # It is zero so don't push to the dual_obj_affine_terms -end - -function add_dual_variable( +function _add_dual_variable( dual_model::MOI.ModelLike, primal_model::MOI.ModelLike, dual_names::DualNames, @@ -148,47 +103,28 @@ 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 - push!(primal_con_to_dual_var_vec, ci => vis) + primal_con_to_dual_var_vec[ci] = vis # Get constraint name ci_name = MOI.get(primal_model, MOI.ConstraintName(), ci) # Add each vi to the dictionary func = MOI.get(primal_model, MOI.ConstraintFunction(), ci) set = MOI.get(primal_model, MOI.ConstraintSet(), ci) for (i, vi) in enumerate(vis) - push_to_dual_obj_aff_terms!( - primal_model, - dual_obj_affine_terms, - vi, - func, - set, - i, - ) - if !is_empty(dual_names) - set_dual_variable_name( - dual_model, - vi, - i, - ci_name, - dual_names.dual_variable_name_prefix, - ) + if !(F <: MOI.VectorOfVariables) + value = set_dot(i, set, T) * _get_normalized_constant(func, set, i) + if !iszero(value) + dual_obj_affine_terms[vi] = value + end + end + if !is_empty(dual_names) && !isempty(ci_name) + pre = dual_names.dual_variable_name_prefix + MOI.set(dual_model, MOI.VariableName(), vi, pre * ci_name * "_$i") end end return con_index end -function set_dual_variable_name( - dual_model::MOI.ModelLike, - vi::MOI.VariableIndex, - i::Int, - ci_name::String, - prefix::String, -) - isempty(ci_name) && return - MOI.set(dual_model, MOI.VariableName(), vi, prefix * ci_name * "_$i") - return -end - -function add_primal_parameter_vars( +function _add_primal_parameter_vars( dual_model::MOI.ModelLike, primal_model::MOI.ModelLike, primal_dual_map::PrimalDualMap{T}, @@ -199,77 +135,38 @@ function add_primal_parameter_vars( ) where {T} # if objective is ignored we only need parameters that appear in the # quadratic objective - if ignore_objective + parameters = if ignore_objective # only crossed terms (parameter times primal variable) of the objective # are required - added = Set{MOI.VariableIndex}() + to_add = Set{MOI.VariableIndex}() for vec in values(primal_objective.quad_cross_parameters) for term in vec - ind = term.variable - if ind in added - # do nothing - else - push!(added, ind) - vi = MOI.add_variable(dual_model) - push_to_primal_parameter_to_dual_parameter!( - primal_dual_map.primal_parameter_to_dual_parameter, - ind, - vi, - ) - # set name - if !is_empty(dual_names) - vi_name = MOI.get(primal_model, MOI.VariableName(), ind) - set_parameter_variable_name( - dual_model, - vi, - vi_name, - dual_names, - ) - end - end + push!(to_add, term.variable) end end - elseif length(variable_parameters) > 0 - vis = MOI.add_variables(dual_model, length(variable_parameters)) - for i in eachindex(vis) - push_to_primal_parameter_to_dual_parameter!( - primal_dual_map.primal_parameter_to_dual_parameter, - variable_parameters[i], + collect(to_add) + else + variable_parameters + end + vis = MOI.add_variables(dual_model, length(parameters)) + for i in eachindex(vis) + primal_dual_map.primal_parameter_to_dual_parameter[parameters[i]] = + vis[i] + if !is_empty(dual_names) + vi_name = MOI.get(primal_model, MOI.VariableName(), parameters[i]) + _set_parameter_variable_name( + dual_model, vis[i], + vi_name, + dual_names, ) - if !is_empty(dual_names) - vi_name = MOI.get( - primal_model, - MOI.VariableName(), - variable_parameters[i], - ) - set_parameter_variable_name( - dual_model, - vis[i], - vi_name, - dual_names, - ) - end end end return end -# Save mapping between primal parameter and dual parameter -function push_to_primal_parameter_to_dual_parameter!( - primal_parameter_to_dual_parameter::Dict{ - MOI.VariableIndex, - MOI.VariableIndex, - }, - vi::MOI.VariableIndex, - vi_dual::MOI.VariableIndex, -) - push!(primal_parameter_to_dual_parameter, vi => vi_dual) - return -end - # Add name to parameter variable -function set_parameter_variable_name( +function _set_parameter_variable_name( dual_model::MOI.ModelLike, vi::MOI.VariableIndex, vi_name::String, @@ -282,7 +179,7 @@ function set_parameter_variable_name( return end -function add_quadratic_slack_vars( +function _add_quadratic_slack_vars( dual_model::MOI.ModelLike, primal_model::MOI.ModelLike, primal_dual_map::PrimalDualMap{T}, @@ -299,15 +196,11 @@ function add_quadratic_slack_vars( else push!(added, ind) vi = MOI.add_variable(dual_model) - push_to_quad_slack!( - primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var, - ind, - vi, - ) - # set name + primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var[ind] = + vi if !is_empty(dual_names) vi_name = MOI.get(primal_model, MOI.VariableName(), ind) - set_quad_slack_name(dual_model, vi, vi_name, dual_names) + _set_quad_slack_name(dual_model, vi, vi_name, dual_names) end end end @@ -315,18 +208,8 @@ function add_quadratic_slack_vars( return end -# Save mapping between primal variable and dual quadratic slack -function push_to_quad_slack!( - dual_quad_slack::Dict{MOI.VariableIndex,MOI.VariableIndex}, - vi::MOI.VariableIndex, - vi_dual::MOI.VariableIndex, -) - push!(dual_quad_slack, vi => vi_dual) - return -end - # set name for dual quadratic slack -function set_quad_slack_name( +function _set_quad_slack_name( dual_model::MOI.ModelLike, vi::MOI.VariableIndex, vi_name::String, diff --git a/src/dualize.jl b/src/dualize.jl index 4f6915cf..cbb7cdf0 100644 --- a/src/dualize.jl +++ b/src/dualize.jl @@ -81,12 +81,12 @@ function dualize( supported_constraints(con_types) # Errors if constraint cant be dualized # Set the dual model objective sense - set_dual_model_sense(dual_problem.dual_model, primal_model) + _set_dual_model_sense(dual_problem.dual_model, primal_model) # Get primal objective in quadratic form # terms already split considering parameters primal_objective = - get_primal_objective(primal_model, variable_parameters, T) + _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 @@ -97,7 +97,7 @@ function dualize( # the respective primal variable will not be a constrained variable (with # respect to that constraint). if consider_constrained_variables - _add_all_constrained_variables( + _select_constrained_variables( dual_problem, primal_model, variable_parameters, @@ -124,7 +124,7 @@ function dualize( # this map is used in `MOI.get(::DualOptimizer,::MOI.ConstraintPrimal,ci)` # that requires extra information in the case that the scalar set # constrains a constant (EqualtTo, GreaterThan, LessThan) - dual_obj_affine_terms = add_dual_vars_in_dual_cones( + dual_obj_affine_terms = _add_dual_vars_in_dual_cones( dual_problem.dual_model, primal_model, dual_problem.primal_dual_map, @@ -136,7 +136,7 @@ function dualize( # primal model. # Fills `primal_parameter_to_dual_parameter` mapping parameters in the primal to parameters # in the dual model. - add_primal_parameter_vars( + _add_primal_parameter_vars( dual_problem.dual_model, primal_model, dual_problem.primal_dual_map, @@ -151,7 +151,7 @@ function dualize( # associated dual slack variable that is created here. # also, `primal_var_in_quad_obj_to_dual_slack_var` is filled, mapping primal variables # (that appear in quadritc objective terms) to dual "slack" variables. - add_quadratic_slack_vars( + _add_quadratic_slack_vars( dual_problem.dual_model, primal_model, dual_problem.primal_dual_map, @@ -166,7 +166,7 @@ function dualize( # Also, fills the link dictionary. # returns `scalar_affine_terms` # because the terms associated to variables that are parameters will be used - # in `get_dual_objective` + # in `_get_dual_objective` scalar_affine_terms = _add_dual_equality_constraints( dual_problem.dual_model, primal_model, @@ -181,7 +181,7 @@ function dualize( # do not add objective else # Fill Dual Objective Coefficients Struct - dual_objective = get_dual_objective( + dual_objective = _get_dual_objective( dual_problem, dual_obj_affine_terms, primal_objective, @@ -189,7 +189,7 @@ function dualize( variable_parameters, ) # Add dual objective to the model - set_dual_objective(dual_problem.dual_model, dual_objective) + _set_dual_objective(dual_problem.dual_model, dual_objective) end return dual_problem end diff --git a/src/objective_coefficients.jl b/src/objective_coefficients.jl index 97e17964..af93163b 100644 --- a/src/objective_coefficients.jl +++ b/src/objective_coefficients.jl @@ -4,11 +4,11 @@ # in the LICENSE.md file or at https://opensource.org/licenses/MIT. """ - set_dual_model_sense!(dual_model::MOI.ModelLike, model::MOI.ModelLike) + _set_dual_model_sense(dual_model::MOI.ModelLike, model::MOI.ModelLike) Set the dual model objective sense. """ -function set_dual_model_sense( +function _set_dual_model_sense( dual_model::MOI.ModelLike, primal_model::MOI.ModelLike, )::Nothing @@ -54,11 +54,11 @@ function _scalar_quadratic_function(func::MOI.VariableIndex, T::Type) end """ - PrimalObjective{T} + _PrimalObjective{T} Primal objective is defined as a `MOI.ScalarAffineFunction` """ -mutable struct PrimalObjective{T} +mutable struct _PrimalObjective{T} obj::MOI.ScalarQuadraticFunction{T} quad_cross_parameters::Dict{ MOI.VariableIndex, @@ -66,7 +66,7 @@ mutable struct PrimalObjective{T} } obj_parametric::Union{MOI.ScalarQuadraticFunction{T},Nothing} - function PrimalObjective{T}(obj) where {T} + function _PrimalObjective{T}(obj) where {T} canonical_obj = _scalar_quadratic_function(obj, T) quad_cross_parameters = Dict{MOI.VariableIndex,Vector{MOI.ScalarAffineTerm{T}}}() @@ -75,45 +75,26 @@ mutable struct PrimalObjective{T} end """ - DualObjective{T} + _DualObjective{T} Dual objective is defined as a `MOI.ScalarAffineFunction`. """ -mutable struct DualObjective{T} +mutable struct _DualObjective{T} obj::MOI.ScalarQuadraticFunction{T} end -const Objective{T} = Union{PrimalObjective{T},DualObjective{T}} - -function get_raw_obj(objective::Objective{T}) where {T} - return objective.obj -end -function get_affine_terms(objective::Objective{T}) where {T} - return objective.obj.affine_terms -end - -function get_primal_objective(primal_model::MOI.ModelLike, T::Type = Float64) - F = MOI.get(primal_model, MOI.ObjectiveFunctionType()) - return _get_primal_objective( - MOI.get(primal_model, MOI.ObjectiveFunction{F}()), - T, - ) -end - -function _get_primal_objective(obj_fun, T::Type) - return PrimalObjective{T}(obj_fun) -end - # allow removing variables from objective function -function get_primal_objective( +function _get_primal_objective( primal_model::MOI.ModelLike, variable_parameters::Vector{MOI.VariableIndex}, T::Type, ) - p_obj = get_primal_objective(primal_model, T) + F = MOI.get(primal_model, MOI.ObjectiveFunctionType()) + p_obj = + _PrimalObjective{T}(MOI.get(primal_model, MOI.ObjectiveFunction{F}())) if length(variable_parameters) > 0 vars_func, quad_cross_params, params_func = - split_variables(p_obj.obj, variable_parameters) + _split_variables(p_obj.obj, variable_parameters) p_obj.obj = vars_func p_obj.quad_cross_parameters = quad_cross_params p_obj.obj_parametric = params_func @@ -121,7 +102,7 @@ function get_primal_objective( return p_obj end -function split_variables( +function _split_variables( func::MOI.ScalarQuadraticFunction{T}, variable_parameters::Vector{MOI.VariableIndex}, ) where {T} @@ -148,9 +129,9 @@ function split_variables( if is_param_1 && is_param_2 push!(quad_params, term) elseif is_param_1 - push_affine_term(quad_cross_params, term, false) + _push_affine_term(quad_cross_params, term, false) elseif is_param_2 - push_affine_term(quad_cross_params, term, true) + _push_affine_term(quad_cross_params, term, true) else push!(quad_vars, term) end @@ -164,7 +145,7 @@ function split_variables( return variables_func, quad_cross_params, parameters_func end -function push_affine_term( +function _push_affine_term( dic, term::MOI.ScalarQuadraticTerm{T}, var_is_first::Bool, @@ -182,19 +163,19 @@ function push_affine_term( end """ - set_dual_objective( + _set_dual_objective( dual_model::MOI.ModelLike, - dual_objective::DualObjective{T}, + dual_objective::_DualObjective{T}, )::Nothing where {T} Add the objective function to the dual model. """ -function set_dual_objective( +function _set_dual_objective( dual_model::MOI.ModelLike, - dual_objective::DualObjective{T}, + dual_objective::_DualObjective{T}, )::Nothing where {T} # Set dual model objective function - raw_obj = get_raw_obj(dual_objective) + raw_obj = dual_objective.obj if MOI.Utilities.number_of_quadratic_terms(T, raw_obj) > 0 MOI.set( dual_model, @@ -212,21 +193,21 @@ function set_dual_objective( end """ - get_dual_objective( + _get_dual_objective( dual_model::MOI.ModelLike, dual_obj_affine_terms::Dict, - primal_objective::PrimalObjective{T}, - )::DualObjective{T} where {T} + primal_objective::_PrimalObjective{T}, + )::_DualObjective{T} where {T} build the dual model objective function from the primal model. """ -function get_dual_objective( +function _get_dual_objective( dual_problem, dual_obj_affine_terms::Dict, - primal_objective::PrimalObjective{T}, + primal_objective::_PrimalObjective{T}, scalar_affine_terms, variable_parameters, -)::DualObjective{T} where {T} +)::_DualObjective{T} where {T} dual_model = dual_problem.dual_model map = dual_problem.primal_dual_map sense_change = ifelse( @@ -318,7 +299,7 @@ function get_dual_objective( saf_dual_objective = MOI.ScalarQuadraticFunction{T}( quad_terms, lin_terms, - MOI.constant(get_raw_obj(primal_objective)), + MOI.constant(primal_objective.obj), ) - return DualObjective{T}(saf_dual_objective) + return _DualObjective{T}(saf_dual_objective) end diff --git a/src/supported.jl b/src/supported.jl index 093fe03c..eb2c62c3 100644 --- a/src/supported.jl +++ b/src/supported.jl @@ -8,6 +8,11 @@ Returns `true` if `Function-in-Set` is supported for Dualization and throws an error if it is not. + + supported_constraints(F::MOI.AbstractFunction, S::MOI.AbstractSet) + +Returns `true` if `Function-in-Set` is supported for Dualization and `false` +if it is not. """ function supported_constraints(con_types::Vector{Tuple{Type,Type}}) for (F, S) in con_types @@ -18,7 +23,7 @@ function supported_constraints(con_types::Vector{Tuple{Type,Type}}) ) end end - return + return true end supported_constraint(::Type, ::Type) = false @@ -42,19 +47,24 @@ end Returns `true` if `MOI.ObjectiveFunctionType()` is supported for Dualization and throws an error if it is not. + + supported_objective(obj_func_type::Type) + +Returns `true` if `obj_func_type` is supported for Dualization and throws an +error if it is not. """ function supported_objective(primal_model::MOI.ModelLike) obj_func_type = MOI.get(primal_model, MOI.ObjectiveFunctionType()) - if !supported_obj(obj_func_type) + if !supported_objective(obj_func_type) error("Objective functions of type $obj_func_type are not implemented") end - return + return true end -supported_obj(::Type) = false +supported_objective(::Type) = false -supported_obj(::Type{MOI.VariableIndex}) = true +supported_objective(::Type{MOI.VariableIndex}) = true -supported_obj(::Type{<:MOI.ScalarAffineFunction}) = true +supported_objective(::Type{<:MOI.ScalarAffineFunction}) = true -supported_obj(::Type{<:MOI.ScalarQuadraticFunction}) = true +supported_objective(::Type{<:MOI.ScalarQuadraticFunction}) = true diff --git a/src/utils.jl b/src/utils.jl index d9a4ea8e..db4e47d2 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -5,14 +5,14 @@ # Some useful wrappers -function get_scalar_term( +function _get_normalized_constant( model::MOI.ModelLike, ci::MOI.ConstraintIndex{MOI.VariableIndex,S}, ) where {S<:MOI.AbstractScalarSet} return [-MOI.constant(MOI.get(model, MOI.ConstraintSet(), ci))] end -function get_scalar_term( +function _get_normalized_constant( model::MOI.ModelLike, ci::MOI.ConstraintIndex{F,S}, ) where {F<:MOI.AbstractScalarFunction,S<:MOI.AbstractScalarSet} @@ -23,7 +23,7 @@ function get_scalar_term( end # This is used to fill the dual objective dictionary -function get_scalar_term( +function _get_normalized_constant( func::MOI.AbstractVectorFunction, ::MOI.AbstractVectorSet, i::Int, @@ -32,7 +32,7 @@ function get_scalar_term( end # This is used to fill the dual objective dictionary -function get_scalar_term( +function _get_normalized_constant( ::MOI.VariableIndex, set::MOI.AbstractScalarSet, i::Int, @@ -41,7 +41,7 @@ function get_scalar_term( end # This is used to fill the dual objective dictionary -function get_scalar_term( +function _get_normalized_constant( func::MOI.AbstractScalarFunction, set::MOI.AbstractScalarSet, i::Int, diff --git a/test/Tests/test_MOI_wrapper.jl b/test/Tests/test_MOI_wrapper.jl index c8741a4a..d1831a7b 100644 --- a/test/Tests/test_MOI_wrapper.jl +++ b/test/Tests/test_MOI_wrapper.jl @@ -108,12 +108,12 @@ end end - @testset "dual_status" begin - @test Dualization.dual_status(MOI.INFEASIBLE) == MOI.DUAL_INFEASIBLE - @test Dualization.dual_status(MOI.DUAL_INFEASIBLE) == MOI.INFEASIBLE - @test Dualization.dual_status(MOI.ALMOST_INFEASIBLE) == + @testset "_dual_status" begin + @test Dualization._dual_status(MOI.INFEASIBLE) == MOI.DUAL_INFEASIBLE + @test Dualization._dual_status(MOI.DUAL_INFEASIBLE) == MOI.INFEASIBLE + @test Dualization._dual_status(MOI.ALMOST_INFEASIBLE) == MOI.ALMOST_DUAL_INFEASIBLE - @test Dualization.dual_status(MOI.ALMOST_DUAL_INFEASIBLE) == + @test Dualization._dual_status(MOI.ALMOST_DUAL_INFEASIBLE) == MOI.ALMOST_INFEASIBLE end diff --git a/test/Tests/test_dual_model_variables.jl b/test/Tests/test_dual_model_variables.jl index eb3c929b..b6e07199 100644 --- a/test/Tests/test_dual_model_variables.jl +++ b/test/Tests/test_dual_model_variables.jl @@ -2,41 +2,3 @@ # # Use of this source code is governed by an MIT-style license that can be found # in the LICENSE.md file or at https://opensource.org/licenses/MIT. - -# Tests of this file not covered in other tests -@testset "dual_model_variables.jl" begin - @testset "push_to_dual_obj_aff_terms!" begin - primal_model = soc1_test() - dual_obj_affine_terms = Dict{MOI.VariableIndex,Float64}() - list = MOI.get( - primal_model, - MOI.ListOfConstraintIndices{ - MOI.VectorOfVariables, - MOI.SecondOrderCone, - }(), - ) - ci = first(list) - func = MOI.get(primal_model, MOI.ConstraintFunction(), ci) - set = MOI.get(primal_model, MOI.ConstraintSet(), ci) - Dualization.push_to_dual_obj_aff_terms!( - primal_model, - dual_obj_affine_terms, - MOI.VariableIndex(1), - func, - set, - 1, - ) - @test isempty(dual_obj_affine_terms) - end - - @testset "set_dual_variable_name" begin - primal_model = soc1_test() - vi = MOI.VariableIndex(1) - Dualization.set_dual_variable_name(primal_model, vi, 1, "con", "") - @test MOI.get(primal_model, MOI.VariableName(), vi) == "con_1" - Dualization.set_dual_variable_name(primal_model, vi, 2, "con", "") - @test MOI.get(primal_model, MOI.VariableName(), vi) == "con_2" - Dualization.set_dual_variable_name(primal_model, vi, 2, "con", "oi") - @test MOI.get(primal_model, MOI.VariableName(), vi) == "oicon_2" - end -end diff --git a/test/Tests/test_objective_coefficients.jl b/test/Tests/test_objective_coefficients.jl index d72cb8db..68636978 100644 --- a/test/Tests/test_objective_coefficients.jl +++ b/test/Tests/test_objective_coefficients.jl @@ -4,38 +4,20 @@ # in the LICENSE.md file or at https://opensource.org/licenses/MIT. @testset "objective_coefficients.jl" begin - @testset "set_dual_model_sense" begin + @testset "_set_dual_model_sense" begin # ERROR: FEASIBILITY_SENSE is not supported - # @test_throws ErrorException Dualization.set_dual_model_sense(lp11_test(), lp11_test()) + # @test_throws ErrorException Dualization._set_dual_model_sense(lp11_test(), lp11_test()) # obj = MOI.ScalarAffineFunction([MOI.ScalarAffineTerm(0.0, MOI.VariableIndex(1))], 0.0) - # @test_throws ErrorException Dualization.PrimalObjective{Float64}(obj) + # @test_throws ErrorException Dualization._PrimalObjective{Float64}(obj) model = lp1_test() @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MIN_SENSE - Dualization.set_dual_model_sense(model, model) + Dualization._set_dual_model_sense(model, model) @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MAX_SENSE model = lp4_test() @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MAX_SENSE - Dualization.set_dual_model_sense(model, model) + Dualization._set_dual_model_sense(model, model) @test MOI.get(model, MOI.ObjectiveSense()) == MOI.MIN_SENSE end - - @testset "get_primal_objective" begin - model = lp1_test() - primal_objective = Dualization.get_primal_objective(model) - - @test Dualization.get_affine_terms(primal_objective)[1] == - MOI.ScalarAffineTerm{Float64}(-4.0, MOI.VariableIndex(2)) - @test MOI.constant(Dualization.get_raw_obj(primal_objective)) == -1.0 - - model = lp10_test() - - @test model.objective.single_variable == MOI.VariableIndex(1) - primal_objective = Dualization.get_primal_objective(model) - @test Dualization.get_affine_terms( - Dualization.get_primal_objective(model), - )[1] == MOI.ScalarAffineTerm{Float64}(1.0, MOI.VariableIndex(1)) - @test MOI.constant(Dualization.get_raw_obj(primal_objective)) == 0.0 - end end diff --git a/test/Tests/test_supported.jl b/test/Tests/test_supported.jl index 96569709..6d48f749 100644 --- a/test/Tests/test_supported.jl +++ b/test/Tests/test_supported.jl @@ -11,9 +11,9 @@ obj_typ_svf = MOI.get(lp1_test(), MOI.ObjectiveFunctionType()) obj_typ_saf = MOI.get(lp10_test(), MOI.ObjectiveFunctionType()) obj_typ_qp = MOI.get(qp1_test(), MOI.ObjectiveFunctionType()) - @test Dualization.supported_obj(obj_typ_svf) - @test Dualization.supported_obj(obj_typ_saf) - @test Dualization.supported_obj(obj_typ_qp) + @test Dualization.supported_objective(obj_typ_svf) + @test Dualization.supported_objective(obj_typ_saf) + @test Dualization.supported_objective(obj_typ_qp) end @testset "supported_constraints" begin From efd4d653d9a02b5ba13b4e20a40cd4db9d78d038 Mon Sep 17 00:00:00 2001 From: joaquimg Date: Tue, 15 Apr 2025 10:36:00 -0700 Subject: [PATCH 11/11] extra minor cleaning --- src/MOI_wrapper.jl | 10 +-- src/dual_model_variables.jl | 160 +++++++++++----------------------- src/objective_coefficients.jl | 3 +- 3 files changed, 58 insertions(+), 115 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 94a3765d..504b04e2 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -259,20 +259,20 @@ function MOI.get( optimizer.dual_problem.dual_model, MOI.ConstraintDual(), ci_dual, - )[idx]::Number + )[idx] else return MOI.get( optimizer.dual_problem.dual_model, MOI.ConstraintDual(), ci_dual, - )::Number + ) end else return -MOI.get( optimizer.dual_problem.dual_model, MOI.ConstraintDual(), primal_dual_map.primal_var_to_dual_con[vi], - )::Number + ) end end @@ -310,7 +310,7 @@ function MOI.get( optimizer.dual_problem.dual_model, MOI.VariablePrimal(), primal_dual_map.primal_con_to_dual_var_vec[ci][], - )::Number + ) end end @@ -362,7 +362,7 @@ function MOI.get( optimizer.dual_problem.dual_model, MOI.ConstraintDual(), ci_dual, - )::Number + ) end else primal_ci_constant = diff --git a/src/dual_model_variables.jl b/src/dual_model_variables.jl index e29f45d0..bbe6e19a 100644 --- a/src/dual_model_variables.jl +++ b/src/dual_model_variables.jl @@ -3,6 +3,29 @@ # Use of this source code is governed by an MIT-style license that can be found # in the LICENSE.md file or at https://opensource.org/licenses/MIT. +# barrier function +function _add_dual_vars_in_dual_cones( + dual_model::MOI.ModelLike, + primal_model::MOI.ModelLike, + primal_dual_map::PrimalDualMap{T}, + dual_names::DualNames, + con_types::Vector{Tuple{Type,Type}}, +) where {T} + dual_obj_affine_terms = Dict{MOI.VariableIndex,T}() + for (F, S) in con_types + _add_dual_vars_in_dual_cones( + dual_obj_affine_terms, + dual_model, + primal_model, + primal_dual_map, + dual_names, + F, + S, + ) + end + return dual_obj_affine_terms +end + function _add_dual_vars_in_dual_cones( dual_obj_affine_terms::Dict{MOI.VariableIndex,T}, dual_model::MOI.ModelLike, @@ -23,87 +46,30 @@ function _add_dual_vars_in_dual_cones( end # Add dual variable to dual cone # Fill a dual objective dictionary - # Fill the primal_con_to_dual_var_vec dictionary - ci_dual = _add_dual_variable( + # Fill the primal_dual_map info + _add_dual_variable( dual_model, primal_model, dual_names, - primal_dual_map.primal_con_to_dual_var_vec, + primal_dual_map, dual_obj_affine_terms, ci, ) - if ci_dual !== nothing - primal_dual_map.primal_con_to_dual_convarcon[ci] = ci_dual - end - _cache_primal_constraint_constant!( - primal_model, - primal_dual_map.primal_con_to_primal_constants_vec, - ci, - ) end return end -# barrier function -function _add_dual_vars_in_dual_cones( - dual_model::MOI.ModelLike, - primal_model::MOI.ModelLike, - primal_dual_map::PrimalDualMap{T}, - dual_names::DualNames, - con_types::Vector{Tuple{Type,Type}}, -) where {T} - dual_obj_affine_terms = Dict{MOI.VariableIndex,T}() - for (F, S) in con_types - _add_dual_vars_in_dual_cones( - dual_obj_affine_terms, - dual_model, - primal_model, - primal_dual_map, - dual_names, - F, - S, - ) - end - return dual_obj_affine_terms -end - -# Utils for primal_con_to_primal_constants_vec dict -function _cache_primal_constraint_constant!( - primal_model::MOI.ModelLike, - primal_con_to_primal_constants_vec::Dict{MOI.ConstraintIndex,Vector{T}}, - ci::MOI.ConstraintIndex{F,S}, -) where {T,F<:MOI.AbstractScalarFunction,S<:MOI.AbstractScalarSet} - primal_con_to_primal_constants_vec[ci] = - _get_normalized_constant(primal_model, ci) - return -end - -function _cache_primal_constraint_constant!( - primal_model::MOI.ModelLike, - primal_con_to_primal_constants_vec::Dict{MOI.ConstraintIndex,Vector{T}}, - ci::MOI.ConstraintIndex{F,S}, -) where {T,F<:MOI.AbstractVectorFunction,S<:MOI.AbstractVectorSet} - # No constants need to be passed to the DualOptimizer in this case, - # because the VectorSet's do not have constants inside them that - # will need to be used in result queries as some ScalarSet's might. - # Hence, don't need to push zero to the dict. - return -end - function _add_dual_variable( dual_model::MOI.ModelLike, primal_model::MOI.ModelLike, dual_names::DualNames, - primal_con_to_dual_var_vec::Dict{ - MOI.ConstraintIndex, - Vector{MOI.VariableIndex}, - }, + primal_dual_map::PrimalDualMap, dual_obj_affine_terms::Dict{MOI.VariableIndex,T}, ci::MOI.ConstraintIndex{F,S}, ) 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_con_to_dual_var_vec[ci] = vis + primal_dual_map.primal_con_to_dual_var_vec[ci] = vis # Get constraint name ci_name = MOI.get(primal_model, MOI.ConstraintName(), ci) # Add each vi to the dictionary @@ -121,7 +87,14 @@ function _add_dual_variable( MOI.set(dual_model, MOI.VariableName(), vi, pre * ci_name * "_$i") end end - return con_index + 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 function _add_primal_parameter_vars( @@ -154,31 +127,15 @@ function _add_primal_parameter_vars( vis[i] if !is_empty(dual_names) vi_name = MOI.get(primal_model, MOI.VariableName(), parameters[i]) - _set_parameter_variable_name( - dual_model, - vis[i], - vi_name, - dual_names, - ) + prefix = + dual_names.parameter_name_prefix == "" ? "param_" : + dual_names.parameter_name_prefix + MOI.set(dual_model, MOI.VariableName(), vi, prefix * vi_name) end end return end -# Add name to parameter variable -function _set_parameter_variable_name( - dual_model::MOI.ModelLike, - vi::MOI.VariableIndex, - vi_name::String, - dual_names, -) - prefix = - dual_names.parameter_name_prefix == "" ? "param_" : - dual_names.parameter_name_prefix - MOI.set(dual_model, MOI.VariableName(), vi, prefix * vi_name) - return -end - function _add_quadratic_slack_vars( dual_model::MOI.ModelLike, primal_model::MOI.ModelLike, @@ -192,34 +149,19 @@ function _add_quadratic_slack_vars( for term in primal_objective.obj.quadratic_terms for ind in [term.variable_1, term.variable_2] if ind in added - #do nothing - else - push!(added, ind) - vi = MOI.add_variable(dual_model) - primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var[ind] = - vi - if !is_empty(dual_names) - vi_name = MOI.get(primal_model, MOI.VariableName(), ind) - _set_quad_slack_name(dual_model, vi, vi_name, dual_names) - end + continue + end + push!(added, ind) + vi = MOI.add_variable(dual_model) + primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var[ind] = vi + if !is_empty(dual_names) + name = MOI.get(primal_model, MOI.VariableName(), ind) + prefix = + dual_names.quadratic_slack_name_prefix == "" ? + "quadslack_" : dual_names.quadratic_slack_name_prefix + MOI.set(dual_model, MOI.VariableName(), vi, prefix * name) end end end return end - -# set name for dual quadratic slack -function _set_quad_slack_name( - dual_model::MOI.ModelLike, - vi::MOI.VariableIndex, - vi_name::String, - dual_names, -) - prefix = if dual_names.quadratic_slack_name_prefix == "" - "quadslack_" - else - dual_names.quadratic_slack_name_prefix - end - MOI.set(dual_model, MOI.VariableName(), vi, prefix * vi_name) - return -end diff --git a/src/objective_coefficients.jl b/src/objective_coefficients.jl index af93163b..f227be14 100644 --- a/src/objective_coefficients.jl +++ b/src/objective_coefficients.jl @@ -32,6 +32,7 @@ function _scalar_quadratic_function( ) where {T} return MOI.Utilities.canonical(func) end + function _scalar_quadratic_function( func::MOI.ScalarAffineFunction{T}, ::Type{T}, @@ -244,7 +245,7 @@ function _get_dual_objective( end # parametric part - # if some varaibles were marked to be parameters then their final + # if some variables were marked to be parameters then their final # processing occurs here. if nothing !== primal_objective.obj_parametric