diff --git a/docs/src/reference.md b/docs/src/reference.md index 584b68ee..e09f1c89 100644 --- a/docs/src/reference.md +++ b/docs/src/reference.md @@ -4,4 +4,7 @@ Dualization.supported_constraints Dualization.supported_objective Dualization.DualNames +Dualization.VariableData +Dualization.ConstraintData +Dualization.PrimalDualMap ``` \ No newline at end of file diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 504b04e2..d9a603f8 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -126,7 +126,7 @@ function _change_constant( model, ci::MOI.ConstraintIndex{<:MOI.ScalarAffineFunction,S}, constant, - idx, + ::Int, ) where {S} MOI.set(model, MOI.ConstraintSet(), ci, S(constant)) return @@ -136,7 +136,7 @@ function _change_constant( model, ci::MOI.ConstraintIndex{<:MOI.VectorAffineFunction}, constant, - idx, + idx::Int, ) func = MOI.get(model, MOI.ConstraintFunction(), ci) constants = copy(func.constant) @@ -159,23 +159,19 @@ function MOI.modify( constant = -constant end vi = obj_change.variable - if haskey(primal_dual_map.primal_convar_to_primal_convarcon_and_index, vi) - ci_primal, index = - primal_dual_map.primal_convar_to_primal_convarcon_and_index[vi] - ci_dual = primal_dual_map.primal_convarcon_to_dual_con[ci_primal] - if ci_dual === NO_CONSTRAINT - return - end + data = get(primal_dual_map.primal_variable_data, vi, nothing) + if data === nothing + # error + elseif data.dual_constraint === nothing + return + elseif data.primal_constrained_variable_constraint !== nothing constant = -constant - else - ci_dual = primal_dual_map.primal_var_to_dual_con[vi] - index = 1 end _change_constant( optimizer.dual_problem.dual_model, - ci_dual, + data.dual_constraint, constant, - index, + data.primal_constrained_variable_index, ) return end @@ -246,32 +242,31 @@ function MOI.get( optimizer::DualOptimizer{T}, ::MOI.VariablePrimal, vi::MOI.VariableIndex, -) where {T} +)::T where {T} primal_dual_map = optimizer.dual_problem.primal_dual_map - if haskey(primal_dual_map.primal_convar_to_primal_convarcon_and_index, vi) - ci_primal, idx = - primal_dual_map.primal_convar_to_primal_convarcon_and_index[vi] - ci_dual = primal_dual_map.primal_convarcon_to_dual_con[ci_primal] - if ci_dual === NO_CONSTRAINT - return zero(T) - elseif ci_dual isa MOI.ConstraintIndex{<:MOI.AbstractVectorFunction} - return MOI.get( - optimizer.dual_problem.dual_model, - MOI.ConstraintDual(), - ci_dual, - )[idx] - else - return MOI.get( - optimizer.dual_problem.dual_model, - MOI.ConstraintDual(), - ci_dual, - ) - end - else + data = get(primal_dual_map.primal_variable_data, vi, nothing) + if data === nothing + # error + elseif data.dual_constraint === nothing + return zero(T) + elseif data.primal_constrained_variable_constraint === nothing return -MOI.get( optimizer.dual_problem.dual_model, MOI.ConstraintDual(), - primal_dual_map.primal_var_to_dual_con[vi], + data.dual_constraint, + ) + elseif data.dual_constraint isa + MOI.ConstraintIndex{<:MOI.AbstractVectorFunction} + return MOI.get( + optimizer.dual_problem.dual_model, + MOI.ConstraintDual(), + data.dual_constraint, + )[data.primal_constrained_variable_index] + else + return MOI.get( + optimizer.dual_problem.dual_model, + MOI.ConstraintDual(), + data.dual_constraint, ) end end @@ -282,16 +277,17 @@ function MOI.get( ci::MOI.ConstraintIndex{F,S}, ) where {F<:MOI.AbstractScalarFunction,S<:MOI.AbstractScalarSet} primal_dual_map = optimizer.dual_problem.primal_dual_map - if haskey(primal_dual_map.primal_convarcon_to_dual_con, ci) - ci_dual = primal_dual_map.primal_convarcon_to_dual_con[ci] - if ci_dual === NO_CONSTRAINT + if haskey(primal_dual_map.primal_constrained_variables, ci) + vi = primal_dual_map.primal_constrained_variables[ci][] + ci_dual = primal_dual_map.primal_variable_data[vi].dual_constraint + if ci_dual === nothing return MOI.Utilities.eval_variables( - primal_dual_map.primal_convarcon_to_dual_function[ci], - ) do vi + primal_dual_map.primal_variable_data[vi].dual_function, + ) do inner_vi return MOI.get( optimizer.dual_problem.dual_model, MOI.VariablePrimal(), - vi, + inner_vi, ) end end @@ -309,7 +305,7 @@ function MOI.get( return MOI.get( optimizer.dual_problem.dual_model, MOI.VariablePrimal(), - primal_dual_map.primal_con_to_dual_var_vec[ci][], + primal_dual_map.primal_constraint_data[ci].dual_variables[], ) end end @@ -320,29 +316,32 @@ function MOI.get( ci::MOI.ConstraintIndex{F,S}, ) where {F<:MOI.AbstractVectorFunction,S<:MOI.AbstractVectorSet} primal_dual_map = optimizer.dual_problem.primal_dual_map - if haskey(primal_dual_map.primal_convarcon_to_dual_con, ci) - ci_dual = primal_dual_map.primal_convarcon_to_dual_con[ci] - if ci_dual === NO_CONSTRAINT - return MOI.Utilities.eval_variables( - primal_dual_map.primal_convarcon_to_dual_function[ci], - ) do vi - return MOI.get( - optimizer.dual_problem.dual_model, - MOI.VariablePrimal(), - vi, - ) - end + if !haskey(primal_dual_map.primal_constraint_data, ci) + vis = primal_dual_map.primal_constrained_variables[ci] + ci_dual = primal_dual_map.primal_variable_data[vis[1]].dual_constraint + if ci_dual === nothing + return [ + MOI.Utilities.eval_variables( + primal_dual_map.primal_variable_data[vi].dual_function, + ) do inner_vi + return MOI.get( + optimizer.dual_problem.dual_model, + MOI.VariablePrimal(), + inner_vi, + ) + end for vi in vis + ] end return MOI.get( optimizer.dual_problem.dual_model, MOI.ConstraintPrimal(), - primal_dual_map.primal_convarcon_to_dual_con[ci], + ci_dual, ) else return MOI.get.( optimizer.dual_problem.dual_model, MOI.VariablePrimal(), - primal_dual_map.primal_con_to_dual_var_vec[ci], + primal_dual_map.primal_constraint_data[ci].dual_variables, ) end end @@ -353,9 +352,11 @@ function MOI.get( ci::MOI.ConstraintIndex{F,S}, ) where {T,F<:MOI.AbstractScalarFunction,S<:MOI.AbstractScalarSet} primal_dual_map = optimizer.dual_problem.primal_dual_map - if haskey(primal_dual_map.primal_convarcon_to_dual_con, ci) - ci_dual = primal_dual_map.primal_convarcon_to_dual_con[ci] - if ci_dual === NO_CONSTRAINT + data = get(primal_dual_map.primal_constraint_data, ci, nothing) + if data === nothing + first_vi = primal_dual_map.primal_constrained_variables[ci][1] + ci_dual = primal_dual_map.primal_variable_data[first_vi].dual_constraint + if ci_dual === nothing return zero(T) else return MOI.get( @@ -365,17 +366,16 @@ function MOI.get( ) end else - primal_ci_constant = - primal_dual_map.primal_con_to_primal_constants_vec[ci][1] + primal_ci_constant = data.primal_set_constants[1] # If it has no key then there is no dual constraint - if !haskey(primal_dual_map.primal_con_to_dual_convarcon, ci) + ci_dual = data.dual_constrained_variable_constraint + if ci_dual === nothing return -primal_ci_constant end - ci_dual_problem = primal_dual_map.primal_con_to_dual_convarcon[ci] return MOI.get( optimizer.dual_problem.dual_model, MOI.ConstraintDual(), - ci_dual_problem, + ci_dual, ) - primal_ci_constant end end @@ -386,13 +386,12 @@ function MOI.get( ci::MOI.ConstraintIndex{F,S}, ) where {T,F<:MOI.AbstractVectorFunction,S<:MOI.AbstractVectorSet} primal_dual_map = optimizer.dual_problem.primal_dual_map - if haskey(primal_dual_map.primal_convarcon_to_dual_con, ci) - ci_dual = primal_dual_map.primal_convarcon_to_dual_con[ci] - if ci_dual === NO_CONSTRAINT - n = MOI.output_dimension( - primal_dual_map.primal_convarcon_to_dual_function[ci], - ) - return zeros(T, n) + data = get(primal_dual_map.primal_constraint_data, ci, nothing) + if data === nothing + vis = primal_dual_map.primal_constrained_variables[ci] + ci_dual = primal_dual_map.primal_variable_data[vis[1]].dual_constraint + if ci_dual === nothing + return zeros(T, length(vis)) else return MOI.get( optimizer.dual_problem.dual_model, @@ -401,18 +400,17 @@ function MOI.get( ) end else + ci_dual = data.dual_constrained_variable_constraint # If it has no key then there is no dual constraint - if !haskey(primal_dual_map.primal_con_to_dual_convarcon, ci) + if ci_dual === nothing # The number of dual variable associated with the primal constraint is the ci dimension - ci_dimension = - length(primal_dual_map.primal_con_to_dual_var_vec[ci]) + ci_dimension = length(data.dual_variables) return zeros(T, ci_dimension) end - ci_dual_problem = primal_dual_map.primal_con_to_dual_convarcon[ci] return MOI.get( optimizer.dual_problem.dual_model, MOI.ConstraintDual(), - ci_dual_problem, + ci_dual, ) end end diff --git a/src/constrained_variables.jl b/src/constrained_variables.jl index 7e86cf15..4ecc19d6 100644 --- a/src/constrained_variables.jl +++ b/src/constrained_variables.jl @@ -14,26 +14,27 @@ function _select_constrained_variables( primal_model, ) params = Set(variable_parameters) + selected_variables = Set{MOI.VariableIndex}() for S in single_or_vector_variables_types _select_constrained_variables( dual_problem.primal_dual_map, primal_model, S, params, + selected_variables, ) end return end -const NO_CONSTRAINT = MOI.ConstraintIndex{Nothing,Nothing}(0) - # Function barrier for the type instability of `F` and `S`. function _select_constrained_variables( - m::PrimalDualMap, + m::PrimalDualMap{T}, primal_model, ::Type{S}, params, -) where {S<:MOI.AbstractVectorSet} + selected_variables, +) where {T,S<:MOI.AbstractVectorSet} cis = MOI.get( primal_model, MOI.ListOfConstraintIndices{MOI.VectorOfVariables,S}(), @@ -44,46 +45,40 @@ function _select_constrained_variables( if all( # no element of the VectorOfVariables is a constrained variable # and not a parameter - vi -> - !haskey(m.primal_convar_to_primal_convarcon_and_index, vi) && - !(vi in params), + vi -> !(vi in selected_variables) && !(vi in params), f.variables, ) - for (i, vi) in enumerate(f.variables) - m.primal_convar_to_primal_convarcon_and_index[vi] = (ci, i) + m.primal_constrained_variables[ci] = f.variables + for vi in f.variables + push!(selected_variables, vi) end - # Placeholder to indicate this constraint is part of constrained variables, - # it will be replaced later with a dual constraints - m.primal_convarcon_to_dual_con[ci] = NO_CONSTRAINT end end return end function _select_constrained_variables( - m::PrimalDualMap, + m::PrimalDualMap{T}, primal_model, ::Type{S}, params, -) where {S<:MOI.AbstractScalarSet} + selected_variables, +) where {T,S<:MOI.AbstractScalarSet} cis = MOI.get( primal_model, MOI.ListOfConstraintIndices{MOI.VariableIndex,S}(), ) for ci in cis - f = MOI.get(primal_model, MOI.ConstraintFunction(), ci) + vi = MOI.get(primal_model, MOI.ConstraintFunction(), ci) # no element of the VectorOfVariables is a constrained variable # and not a parameter - if !haskey(m.primal_convar_to_primal_convarcon_and_index, f) && - !(f in params) + if !(vi in selected_variables) && !(vi in params) set = MOI.get(primal_model, MOI.ConstraintSet(), ci) if !iszero(MOI.constant(set)) continue end - m.primal_convar_to_primal_convarcon_and_index[f] = (ci, 1) - # Placeholder to indicate this constraint is part of constrained variables, - # it will be replaced later with a dual constraints - m.primal_convarcon_to_dual_con[ci] = NO_CONSTRAINT + m.primal_constrained_variables[ci] = [vi] + push!(selected_variables, vi) end end return diff --git a/src/dual_equality_constraints.jl b/src/dual_equality_constraints.jl index e6e5d133..6e68e203 100644 --- a/src/dual_equality_constraints.jl +++ b/src/dual_equality_constraints.jl @@ -6,7 +6,7 @@ function _add_dual_equality_constraints( dual_model::MOI.ModelLike, primal_model::MOI.ModelLike, - primal_dual_map::PrimalDualMap, + primal_dual_map::PrimalDualMap{T}, dual_names::DualNames, primal_objective::_PrimalObjective{T}, con_types::Vector{Tuple{Type,Type}}, @@ -27,7 +27,7 @@ function _add_dual_equality_constraints( # TODO: flip these signs a priorie instead of require post processing later scalar_affine_terms = _get_scalar_affine_terms( primal_model, - primal_dual_map.primal_con_to_dual_var_vec, + primal_dual_map.primal_constraint_data, all_variables, con_types, T, @@ -59,32 +59,31 @@ function _add_dual_equality_constraints( ) # Constrained variables - for ci in keys(primal_dual_map.primal_convarcon_to_dual_con) + for ci in keys(primal_dual_map.primal_constrained_variables) # Add constraints associated with constrained variables # These are constraints that will not be regular equality constraints # they will be function-in-set, where set is the dual set of the # primal constrained variable. + # If the dual set is Reals, the constraint is not added, butthe function + # is cached inthe primal dual map. # TODO: flip these signs a priori instead of requiring post-processing later _add_constrained_variable_constraint( dual_model, primal_model, - primal_dual_map.primal_convarcon_to_dual_function, - primal_dual_map.primal_convarcon_to_dual_con, + primal_dual_map, ci, scalar_affine_terms, scalar_terms, sense_change, - T, dual_names, ) end # Free variables for primal_vi in non_parameter_variables - if haskey( - primal_dual_map.primal_convar_to_primal_convarcon_and_index, - primal_vi, - ) + data = get(primal_dual_map.primal_variable_data, primal_vi, nothing) + if data !== nothing && + data.primal_constrained_variable_constraint !== nothing continue # constrained variable end # Add equality constraint @@ -113,7 +112,8 @@ function _add_dual_equality_constraints( ) end # Add primal variable to dual contraint to the link dictionary - primal_dual_map.primal_var_to_dual_con[primal_vi] = dual_ci + primal_dual_map.primal_variable_data[primal_vi] = + VariableData{T}(nothing, -1, dual_ci, nothing) end return scalar_affine_terms end @@ -124,41 +124,39 @@ end function _add_constrained_variable_constraint( dual_model, primal_model, - zero_map, - ci_map, + primal_dual_map::PrimalDualMap{T}, ci::MOI.ConstraintIndex{MOI.VectorOfVariables,MOI.Zeros}, scalar_affine_terms, scalar_terms, sense_change, - ::Type{T}, ) where {T} # The dual is `Reals`, adding a constraint `func`-in-`Reals` is equivalent # to not adding any constraint. - func_primal = MOI.get(primal_model, MOI.ConstraintFunction(), ci) - zero_map[ci] = MOI.Utilities.vectorize([ - MOI.ScalarAffineFunction( - MOI.Utilities.operate_terms(-, scalar_affine_terms[primal_vi]), - sense_change * get(scalar_terms, primal_vi, zero(T)), - ) for primal_vi in func_primal.variables - ]) + vis = primal_dual_map.primal_constrained_variables[ci] + for (i, vi) in enumerate(vis) + dual_function = MOI.ScalarAffineFunction( + MOI.Utilities.operate_terms(-, scalar_affine_terms[vi]), + sense_change * get(scalar_terms, vi, zero(T)), + ) + primal_dual_map.primal_variable_data[vi] = + VariableData{T}(ci, i, nothing, dual_function) + end return end function _add_constrained_variable_constraint( dual_model, primal_model, - zero_map, - ci_map, + primal_dual_map::PrimalDualMap{T}, ci::MOI.ConstraintIndex{MOI.VectorOfVariables}, scalar_affine_terms, scalar_terms, sense_change, - ::Type{T}, dual_names::DualNames, ) where {T} + vis = primal_dual_map.primal_constrained_variables[ci] set_primal = MOI.get(primal_model, MOI.ConstraintSet(), ci) set_dual = MOI.dual_set(set_primal) - func_primal = MOI.get(primal_model, MOI.ConstraintFunction(), ci) func_dual = MOI.Utilities.vectorize([ MOI.ScalarAffineFunction( MOI.Utilities.operate_term.( @@ -169,9 +167,13 @@ function _add_constrained_variable_constraint( sense_change * inv(set_dot(i, set_primal, T)) * get(scalar_terms, primal_vi, zero(T)), - ) for (i, primal_vi) in enumerate(func_primal.variables) + ) for (i, primal_vi) in enumerate(vis) ]) - ci_map[ci] = MOI.add_constraint(dual_model, func_dual, set_dual) + dual_ci = MOI.add_constraint(dual_model, func_dual, set_dual) + for (i, vi) in enumerate(vis) + primal_dual_map.primal_variable_data[vi] = + VariableData{T}(ci, i, dual_ci, nothing) + end if !is_empty(dual_names) @warn( "dual names for constrained vector of variables not supported yet." @@ -183,56 +185,54 @@ end function _add_constrained_variable_constraint( dual_model, primal_model, - zero_map, - ci_map, - ci::MOI.ConstraintIndex{MOI.VariableIndex,<:MOI.EqualTo}, + primal_dual_map::PrimalDualMap{T}, + ci::MOI.ConstraintIndex{MOI.VariableIndex,MOI.EqualTo{T}}, scalar_affine_terms, scalar_terms, sense_change, - ::Type{T}, dual_names::DualNames, ) where {T} + vi = primal_dual_map.primal_constrained_variables[ci][] # Nothing to add as the set is `EqualTo`. - func_primal = MOI.get(primal_model, MOI.ConstraintFunction(), ci) - primal_vi = func_primal - zero_map[ci] = MOI.ScalarAffineFunction( - MOI.Utilities.operate_terms(-, scalar_affine_terms[primal_vi]), - sense_change * get(scalar_terms, primal_vi, zero(T)), + dual_function = MOI.ScalarAffineFunction( + MOI.Utilities.operate_terms(-, scalar_affine_terms[vi]), + sense_change * get(scalar_terms, vi, zero(T)), ) + primal_dual_map.primal_variable_data[vi] = + VariableData{T}(ci, 0, nothing, dual_function) return end function _add_constrained_variable_constraint( dual_model, primal_model, - zero_map, - ci_map, + primal_dual_map::PrimalDualMap{T}, ci::MOI.ConstraintIndex{MOI.VariableIndex}, scalar_affine_terms, scalar_terms, sense_change, - ::Type{T}, dual_names::DualNames, ) where {T} - func_primal = MOI.get(primal_model, MOI.ConstraintFunction(), ci) - primal_vi = func_primal + vi = primal_dual_map.primal_constrained_variables[ci][] func_dual = MOI.ScalarAffineFunction( - MOI.Utilities.operate_terms(-, scalar_affine_terms[primal_vi]), - sense_change * get(scalar_terms, primal_vi, zero(T)), + MOI.Utilities.operate_terms(-, scalar_affine_terms[vi]), + sense_change * get(scalar_terms, vi, zero(T)), ) set_primal = MOI.get(primal_model, MOI.ConstraintSet(), ci) set_dual = _dual_set(set_primal) - ci_map[ci] = MOI.Utilities.normalize_and_add_constraint( + dual_ci = MOI.Utilities.normalize_and_add_constraint( dual_model, func_dual, set_dual, ) + primal_dual_map.primal_variable_data[vi] = + VariableData{T}(ci, 0, dual_ci, nothing) if !is_empty(dual_names) _set_dual_constraint_name( dual_model, primal_model, - primal_vi, - ci_map[ci], + vi, + dual_ci, dual_names.dual_constraint_name_prefix, ) end @@ -339,10 +339,7 @@ function _fill_scalar_affine_terms!( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_con_to_dual_var_vec::Dict{ - MOI.ConstraintIndex, - Vector{MOI.VariableIndex}, - }, + primal_constraint_data, primal_model::MOI.ModelLike, ::Type{F}, ::Type{S}, @@ -350,7 +347,7 @@ function _fill_scalar_affine_terms!( for ci in MOI.get(primal_model, MOI.ListOfConstraintIndices{F,S}()) _fill_scalar_affine_terms!( scalar_affine_terms, - primal_con_to_dual_var_vec, + primal_constraint_data, primal_model, ci, ) @@ -360,10 +357,7 @@ end function _get_scalar_affine_terms( primal_model::MOI.ModelLike, - primal_con_to_dual_var_vec::Dict{ - MOI.ConstraintIndex, - Vector{MOI.VariableIndex}, - }, + primal_constraint_data, variables::Vector{MOI.VariableIndex}, con_types::Vector{Tuple{Type,Type}}, ::Type{T}, @@ -375,7 +369,7 @@ function _get_scalar_affine_terms( for (F, S) in con_types _fill_scalar_affine_terms!( scalar_affine_terms, - primal_con_to_dual_var_vec, + primal_constraint_data, primal_model, F, S, @@ -400,16 +394,13 @@ function _fill_scalar_affine_terms!( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_con_to_dual_var_vec::Dict{ - MOI.ConstraintIndex, - Vector{MOI.VariableIndex}, - }, + primal_constraint_data, primal_model::MOI.ModelLike, ci::MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},S}, ) where {T,S<:Union{MOI.GreaterThan{T},MOI.LessThan{T},MOI.EqualTo{T}}} moi_function = MOI.get(primal_model, MOI.ConstraintFunction(), ci) for term in moi_function.terms - dual_vi = primal_con_to_dual_var_vec[ci][1] # In this case we only have one vi + dual_vi = primal_constraint_data[ci].dual_variables[1] # In this case we only have one vi _push_to_scalar_affine_terms!( scalar_affine_terms[term.variable], MOI.coefficient(term), @@ -424,22 +415,19 @@ function _fill_scalar_affine_terms!( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_con_to_dual_var_vec::Dict{ - MOI.ConstraintIndex, - Vector{MOI.VariableIndex}, - }, + primal_constraint_data, primal_model::MOI.ModelLike, ci::MOI.ConstraintIndex{MOI.VariableIndex,S}, ) where {T,S<:Union{MOI.GreaterThan{T},MOI.LessThan{T},MOI.EqualTo{T}}} - dual_var = get(primal_con_to_dual_var_vec, ci, nothing) - if dual_var === nothing + data = get(primal_constraint_data, ci, nothing) + if data === nothing # No variables created as the primal constraint is the constraint # of a constrained variable. Hence, its duality information goes to # the dual constraint associated to that primal variable. return end moi_function = MOI.get(primal_model, MOI.ConstraintFunction(), ci) - dual_vi = dual_var[1] # In this case we only have one vi + dual_vi = data.dual_variables[1] # In this case we only have one vi _push_to_scalar_affine_terms!( scalar_affine_terms[moi_function], one(T), @@ -453,17 +441,14 @@ function _fill_scalar_affine_terms!( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_con_to_dual_var_vec::Dict{ - MOI.ConstraintIndex, - Vector{MOI.VariableIndex}, - }, + primal_constraint_data, primal_model::MOI.ModelLike, ci::MOI.ConstraintIndex{MOI.VectorAffineFunction{T},S}, ) where {T,S<:MOI.AbstractVectorSet} moi_function = MOI.get(primal_model, MOI.ConstraintFunction(), ci) set = MOI.get(primal_model, MOI.ConstraintSet(), ci) for term in moi_function.terms - dual_vi = primal_con_to_dual_var_vec[ci][term.output_index] + dual_vi = primal_constraint_data[ci].dual_variables[term.output_index] # term.output_index is the row of the MOI.VectorAffineFunction, # it corresponds to the dual variable associated with this constraint _push_to_scalar_affine_terms!( @@ -480,15 +465,12 @@ function _fill_scalar_affine_terms!( MOI.VariableIndex, Vector{MOI.ScalarAffineTerm{T}}, }, - primal_con_to_dual_var_vec::Dict{ - MOI.ConstraintIndex, - Vector{MOI.VariableIndex}, - }, + primal_constraint_data, primal_model::MOI.ModelLike, ci::MOI.ConstraintIndex{MOI.VectorOfVariables,S}, ) where {T,S<:MOI.AbstractVectorSet} - dual_vars = get(primal_con_to_dual_var_vec, ci, nothing) - if dual_vars === nothing + data = get(primal_constraint_data, ci, nothing) + if data === nothing # No variables created as the primal constraint is the constraint # of a constrained variable. Hence, its duality information goes to # the dual constraint associated to that primal variable. @@ -497,7 +479,7 @@ function _fill_scalar_affine_terms!( moi_function = MOI.get(primal_model, MOI.ConstraintFunction(), ci) set = MOI.get(primal_model, MOI.ConstraintSet(), ci) for (i, variable) in enumerate(moi_function.variables) - dual_vi = dual_vars[i] + dual_vi = data.dual_variables[i] _push_to_scalar_affine_terms!( scalar_affine_terms[variable], set_dot(i, set, T) * one(T), diff --git a/src/dual_model_variables.jl b/src/dual_model_variables.jl index bbe6e19a..fe44042f 100644 --- a/src/dual_model_variables.jl +++ b/src/dual_model_variables.jl @@ -37,8 +37,7 @@ function _add_dual_vars_in_dual_cones( ) where {T,F,S} for ci in MOI.get(primal_model, MOI.ListOfConstraintIndices{F,S}()) # If `F` not one of these two, we can skip the `haskey` check. - if (F === MOI.VectorOfVariables || F === MOI.VariableIndex) && - haskey(primal_dual_map.primal_convarcon_to_dual_con, ci) + if haskey(primal_dual_map.primal_constrained_variables, ci) # primal constraints that are the main constraints of # constrained variables have no dual variable associated # bacause they associated with dual constraints @@ -69,7 +68,13 @@ function _add_dual_variable( ) where {T,F<:MOI.AbstractFunction,S<:MOI.AbstractSet} vis, con_index = _add_dual_cone_constraint(dual_model, primal_model, ci) # Add the map of the added dual variable to the relationated constraint - primal_dual_map.primal_con_to_dual_var_vec[ci] = vis + set_constant = if F <: MOI.AbstractScalarFunction + _get_normalized_constant(primal_model, ci) + else + T[] + end + primal_dual_map.primal_constraint_data[ci] = + ConstraintData(set_constant, vis, con_index) # Get constraint name ci_name = MOI.get(primal_model, MOI.ConstraintName(), ci) # Add each vi to the dictionary @@ -87,13 +92,6 @@ function _add_dual_variable( MOI.set(dual_model, MOI.VariableName(), vi, pre * ci_name * "_$i") end end - if con_index !== nothing - primal_dual_map.primal_con_to_dual_convarcon[ci] = con_index - end - if F <: MOI.AbstractScalarFunction - primal_dual_map.primal_con_to_primal_constants_vec[ci] = - _get_normalized_constant(primal_model, ci) - end return end diff --git a/src/dualize.jl b/src/dualize.jl index cbb7cdf0..9c490c97 100644 --- a/src/dualize.jl +++ b/src/dualize.jl @@ -89,13 +89,15 @@ function dualize( _get_primal_objective(primal_model, variable_parameters, T) # Cache information of which primal variables are `constrained_variables` - # creating a map: primal_convar_to_primal_convarcon_and_index, from original primal vars to original - # constrains and their internal index (if vector constrains), 1 otherwise. - # Also, initializes the map: `primal_convarcon_to_dual_con`, from original primal ci - # to the dual constraint (latter is initilized as empty at this point). + # filling the a map: primal_variable_data, from original primal vars to: + # 1) constrained variable constraint (if constrained) and 2) index (if + # vector and constrained) and 3) dual constraint (if dual set not `Reals`), + # 4) dual function (if dual set is `Reals`). + # Items 3 and 4 are initialized with `nothing` at this point. # If the Set constant of a MOI.VariableIndex-in-Set constraint is non-zero, # the respective primal variable will not be a constrained variable (with # respect to that constraint). + # Constrained variables are registered in `primal_constrained_variables`. if consider_constrained_variables _select_constrained_variables( dual_problem, diff --git a/src/structures.jl b/src/structures.jl index 5b5c7710..64775fc4 100644 --- a/src/structures.jl +++ b/src/structures.jl @@ -25,138 +25,127 @@ MOI.Utilities.@model( (MOI.VectorAffineFunction,) ) +""" + VariableData{T} + +Data structure used in `PrimalDualMap` to hold information about primal +variables and their dual counterparts. + + * `primal_constrained_variable_constraint::Union{Nothing,MOI.ConstraintIndex}`: + if primal variable is chosen to be a constrained variable by + Dualization.jl, then this value is different from nothing. + + * `primal_constrained_variable_index::Int`: if variable is a scalar + constrained variable then it is 0. If variable is not a constrained variable + then it is -1. If variable is part of a vector constrained variable, then + this is the position in that vector. + + * `dual_constraint::Union{Nothing,MOI.ConstraintIndex}`: dual constraint + associated with the variable. If the variable is not constrained then the + set is EqualTo{T}(zero(T)). If the variable is a constrained variable then + the set is the dual set of the constrained variable set. If the dual set is + `Reals` then the field is kept as `nothing` as teh constraint is not added. + + * `dual_function::Union{Nothing,MOI.ScalarAffineFunction{T}}`: if the + constrained variable is `VectorOfVariables`-in-`Zeros` or + `VariableIndex`-in-`EqualTo(zero(T))` then the dual is `func`-in-`Reals`, + which is "irrelevant" to the model. But this information is cached for + completeness of the `DualOptimizer` for `get`ting `ConstraintDuals`. + +To got from the constrained variable constraint to the primal variable, use the +`primal_constrained_variables` field of `PrimalDualMap`. + +See also `PrimalDualMap` and `ConstraintData`. +""" +struct VariableData{T} + primal_constrained_variable_constraint::Union{Nothing,MOI.ConstraintIndex} + primal_constrained_variable_index::Int + dual_constraint::Union{Nothing,MOI.ConstraintIndex} + dual_function::Union{Nothing,MOI.ScalarAffineFunction{T}} +end + +# constraints of primal constrained variables are not here +""" + ConstraintData{T} + +Data structure used in `PrimalDualMap` to hold information about primal +constraints and their dual counterparts. + +Constraint indices for constrained variables are not in this structure. They are +added in the `primal_constrained_variables` field of `PrimalDualMap`. + + * `primal_set_constants::Vector{T}`: a vector of primal set constants that are + used in MOI getters. This is used to get the primal constants of the primal + constraints. + + * `dual_variables::Vector{MOI.VariableIndex}`: vector of dual variables. If + primal constraint is scalar then, the vector has length = 1. + + * `dual_constrained_variable_constraint::Union{Nothing,MOI.ConstraintIndex}`: + if primal set is `EqualTo` or `Zeros`, then the dual constraint is `Reals` + then the dual variable is free (no constraint in the dual model). +""" +struct ConstraintData{T} + primal_set_constants::Vector{T} + dual_variables::Vector{MOI.VariableIndex} + dual_constrained_variable_constraint::Union{Nothing,MOI.ConstraintIndex} +end + """ PrimalDualMap{T} Maps information from all structures of the primal to the dual model. -The following abbreviations are used in the maps: +Main maps: -* `var`: variable index -* `con`: constraint index -* `convar`: constrained variable, variable index -* `convarcon`: constrained variable, constraint index + * `primal_variable_data::Dict{MOI.VariableIndex,Dualization.VariableData{T}}`: + maps primal variable indices to their data. The data is a structure that + contains information about the primal variable and its dual counterpart. + In particular, it contains the primal constrained variable constraint index, + the primal constrained variable index, the dual constraint index and the + primal function for the case of constraints that are not added in the dual. -Main maps: + * `primal_constraint_data::Dict{MOI.ConstraintIndex,Dualization.ConstraintData{T}}`: + maps primal constraint indices to their data. The data is a structure that + contains information about the primal constraint and its dual counterpart. + In particular, it contains the primal set constants, the dual variables and + the dual constrained variable constraint index. - * `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). - - 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). - - note: from the above two maps, we can get primal_convar_to_dual_con_and_index - - * `primal_var_to_dual_con::Dict{MOI.VariableIndex,MOI.ConstraintIndex}`: maps - "free" primal variables to their associated dual (equality) constraints. - Free variables as opposed to constrained variables. Note that Dualization - will select automatically which variables are free and which are - constrained. - - note: from the above three maps, we can get primal_var_to_dual_con_and_index - - # TODO: idea, for simmetry, keep only - - primal_convarcon_to_dual_con (as is), analogous to primal_con_to_dual_convarcon - - primal_var_to_dual_con_and_index, analogous to primal_con_to_dual_var_vec - -- the second implicitly tracks which vars were decided to be convar's - - * `primal_con_to_dual_var_vec::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}`: - maps primal constraint indices to vectors of dual variable indices. For - scalar constraints those vectors will be single element vectors. - 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: - primal_con_to_dual_var_vec_and_convarcon - - * `primal_con_to_dual_convarcon::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}`: - maps regular primal constraints to dual constrained variable. If the primal - constraint's set is EqualTo or Zeros, no constraint is added in the dual - variable (the dual variable is said to be free). - Primal constrained variables constraints (the main ones) are not in this - map. However, `VariableIndex`-in-set and `VectorOfVariables`-in-set might - appear in this map if they were not chosen as the main ones. - The keys are similar to the (# primal_con_to_dual_var_vec) map, except - that `VariableIndex`-in-`EqualTo(zero(T))` and `VectorOfVariables`-in-`Zeros` - are not in this map, as the dual constraint would belong to the `Reals` set, - and would be innocuous (hence, not added). - - Additional helper maps: - - * `primal_con_to_primal_constants_vec::Dict{MOI.ConstraintIndex,Vector{T}}`: maps primal - constraints to their respective constants, which might be inside the set. - This map is used in `MOI.get(::DualOptimizer,::MOI.ConstraintPrimal,ci)` - that requires extra information in the case that the scalar set constains - a constant (`EqualtTo`, `GreaterThan`, `LessThan`). - - * `primal_parameter_to_dual_parameter::Dict{MOI.VariableIndex,MOI.VariableIndex}`: maps - parameters in the primal to parameters in the dual model. - - * `primal_convarcon_to_dual_function::Dict{MOI.ConstraintIndex,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_constrained_variables::Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}`: + maps primal constrained variable constraint indices to their primal + constrained variables. + +Addtional maps + + * `primal_parameter_to_dual_parameter::Dict{MOI.VariableIndex,MOI.VariableIndex}`: + maps parameters in the primal model to parameters in the dual model. * `primal_var_in_quad_obj_to_dual_slack_var::Dict{MOI.VariableIndex,MOI.VariableIndex}`: maps primal variables (that appear in quadratic objective terms) to dual "slack" variables. These primal variables might appear in other maps. - Future name: primal_var_in_quad_obj_to_dual_slack_var """ mutable struct PrimalDualMap{T} - primal_convar_to_primal_convarcon_and_index::Dict{ - MOI.VariableIndex, - Tuple{MOI.ConstraintIndex,Int}, - } - primal_convarcon_to_dual_con::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex} - primal_var_to_dual_con::Dict{MOI.VariableIndex,MOI.ConstraintIndex} - primal_con_to_dual_var_vec::Dict{ + primal_variable_data::Dict{MOI.VariableIndex,VariableData{T}} + primal_constraint_data::Dict{MOI.ConstraintIndex,ConstraintData{T}} + primal_constrained_variables::Dict{ MOI.ConstraintIndex, Vector{MOI.VariableIndex}, } - primal_con_to_dual_convarcon::Dict{MOI.ConstraintIndex,MOI.ConstraintIndex} - - primal_con_to_primal_constants_vec::Dict{MOI.ConstraintIndex,Vector{T}} primal_parameter_to_dual_parameter::Dict{ MOI.VariableIndex, MOI.VariableIndex, } - primal_convarcon_to_dual_function::Dict{ - MOI.ConstraintIndex, - Union{MOI.VectorAffineFunction{T},MOI.ScalarAffineFunction{T}}, - } primal_var_in_quad_obj_to_dual_slack_var::Dict{ MOI.VariableIndex, MOI.VariableIndex, } - function PrimalDualMap{T}() where {T} return new( - Dict{MOI.VariableIndex,Tuple{MOI.ConstraintIndex,Int}}(), - Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}(), - Dict{MOI.VariableIndex,MOI.ConstraintIndex}(), + Dict{MOI.VariableIndex,VariableData{T}}(), + Dict{MOI.ConstraintIndex,ConstraintData{T}}(), Dict{MOI.ConstraintIndex,Vector{MOI.VariableIndex}}(), - Dict{MOI.ConstraintIndex,MOI.ConstraintIndex}(), - Dict{MOI.ConstraintIndex,Vector{T}}(), + # Dict{MOI.VariableIndex,MOI.VariableIndex}(), - Dict{ - MOI.ConstraintIndex, - Union{MOI.VectorAffineFunction{T},MOI.ScalarAffineFunction{T}}, - }(), Dict{MOI.VariableIndex,MOI.VariableIndex}(), ) end @@ -164,29 +153,80 @@ end function Base.getproperty(m::PrimalDualMap{T}, name::Symbol) where {T} if name === :constrained_var_idx - @warn "constrained_var_idx field is deprecated, use primal_convar_to_primal_convarcon_and_index instead" - return getfield(m, :primal_convar_to_primal_convarcon_and_index) + error( + """ + Field `constrained_var_idx` was removed. + From a primal variable index, use the field `primal_variable_data`. + In the data structures returned the constraint can be found at + `primal_constrained_variable_constraint` and the index at + `primal_constrained_variable_index`. + """, + ) elseif name === :constrained_var_dual - @warn "constrained_var_dual field is deprecated, use primal_convarcon_to_dual_con instead" - return getfield(m, :primal_convarcon_to_dual_con) + error( + """ + Field `constrained_var_dual` was removed. + From a primal constrained variable constraint index, use the field + `primal_constrained_variables` to obtain the primal varaibles. + Then, from the primal variable index, use the field + `primal_variable_data`. + In the data structures returned, the constraint can be found at + `dual_constraint`. + """, + ) elseif name === :primal_var_dual_con - @warn "primal_var_dual_con field is deprecated, use primal_var_to_dual_con instead" - return getfield(m, :primal_var_to_dual_con) + error( + """ + Field `primal_var_dual_con` was removed. + From a primal variable index, use the field `primal_variable_data`. + In the data structures returned, the constraint can be found at + `dual_constraint`. + """, + ) elseif name === :primal_con_dual_var - @warn "primal_con_dual_var field is deprecated, use primal_con_to_dual_var_vec instead" - return getfield(m, :primal_con_to_dual_var_vec) + error( + """ + Field `primal_con_dual_var` was removed. + From a primal constraint index, use the field + `primal_constraint_data`. + In the data structure returned, the dual variables can be found at + `dual_varaibles`. + """, + ) elseif name === :primal_con_dual_con - @warn "primal_con_dual_con field is deprecated, use primal_con_to_dual_convarcon instead" - return getfield(m, :primal_con_to_dual_convarcon) + error( + """ + Field `primal_con_dual_con` was removed. + From a primal constraint index, use the field + `primal_constraint_data`. + In the data structure returned, the dual constrained variable + constraint can be found at `dual_constrained_variable_constraint`. + """, + ) elseif name === :primal_con_constants - @warn "primal_con_constants field is deprecated, use primal_con_to_primal_constants_vec instead" - return getfield(m, :primal_con_to_primal_constants_vec) + error( + """ + Field `primal_con_constants` was removed. + From a primal constraint index, use the field `primal_constraint_data`. + In the data structure returned, the primal set constants can be found + at `primal_set_constants`. + """, + ) + elseif name === :constrained_var_zero + error( + """ + Field `constrained_var_zero` was removed. + From a primal constrained variable constraint index, use the field + `primal_constrained_variables` to obtain the primal varaibles. + Then, from the primal variable index, use the field + `primal_variable_data`. + In the data structure returned, the primal function can be found at + `dual_function`. + """, + ) elseif name === :primal_parameter @warn "primal_parameter field is deprecated, use primal_parameter_to_dual_parameter instead" return getfield(m, :primal_parameter_to_dual_parameter) - elseif name === :constrained_var_zero - @warn "constrained_var_zero field is deprecated, use primal_convarcon_to_dual_function instead" - return getfield(m, :primal_convarcon_to_dual_function) elseif name === :primal_var_dual_quad_slack @warn "primal_var_dual_quad_slack field is deprecated, use primal_var_in_quad_obj_to_dual_slack_var instead" return getfield(m, :primal_var_in_quad_obj_to_dual_slack_var) @@ -196,27 +236,19 @@ 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, - ) && - 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) && + return isempty(primal_dual_map.primal_variable_data) && + isempty(primal_dual_map.primal_constraint_data) && + isempty(primal_dual_map.primal_constrained_variables) && + # isempty(primal_dual_map.primal_parameter_to_dual_parameter) && isempty(primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var) end function empty!(primal_dual_map::PrimalDualMap) - 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_variable_data) + Base.empty!(primal_dual_map.primal_constraint_data) + Base.empty!(primal_dual_map.primal_constrained_variables) + # Base.empty!(primal_dual_map.primal_parameter_to_dual_parameter) Base.empty!(primal_dual_map.primal_var_in_quad_obj_to_dual_slack_var) return diff --git a/test/Tests/test_dual_names.jl b/test/Tests/test_dual_names.jl index e9b45f51..9278846c 100644 --- a/test/Tests/test_dual_names.jl +++ b/test/Tests/test_dual_names.jl @@ -32,23 +32,29 @@ dual_model = dual_problem.dual_model primal_dual_map = dual_problem.primal_dual_map # Query variable names - vi_1 = primal_dual_map.primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + vi_1 = primal_dual_map.primal_constraint_data[MOI.ConstraintIndex{ MOI.VariableIndex, MOI.GreaterThan{Float64}, }( 1, - )][1] - vi_2 = primal_dual_map.primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + )].dual_variables[1] + vi_2 = primal_dual_map.primal_constraint_data[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}, }( 1, - )][1] + )].dual_variables[1] @test MOI.get(dual_model, MOI.VariableName(), vi_1) == "" @test MOI.get(dual_model, MOI.VariableName(), vi_2) == "" # Query constraint names - ci_1 = primal_dual_map.primal_var_to_dual_con[MOI.VariableIndex(1)] - ci_2 = primal_dual_map.primal_var_to_dual_con[MOI.VariableIndex(2)] + ci_1 = + primal_dual_map.primal_variable_data[MOI.VariableIndex( + 1, + )].dual_constraint + ci_2 = + primal_dual_map.primal_variable_data[MOI.VariableIndex( + 2, + )].dual_constraint @test MOI.get(dual_model, MOI.ConstraintName(), ci_1) == "" @test MOI.get(dual_model, MOI.ConstraintName(), ci_2) == "" @@ -59,22 +65,28 @@ dual_model = dual_problem.dual_model primal_dual_map = dual_problem.primal_dual_map # Query variable names - vi_1 = primal_dual_map.primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + vi_1 = primal_dual_map.primal_constraint_data[MOI.ConstraintIndex{ MOI.VariableIndex, MOI.GreaterThan{Float64}, }( 1, - )][1] - vi_2 = primal_dual_map.primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + )].dual_variables[1] + vi_2 = primal_dual_map.primal_constraint_data[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}, }( 1, - )][1] + )].dual_variables[1] @test MOI.get(dual_model, MOI.VariableName(), vi_2) == "dualvar_lessthan_1" # Query constraint names - ci_1 = primal_dual_map.primal_var_to_dual_con[MOI.VariableIndex(1)] - ci_2 = primal_dual_map.primal_var_to_dual_con[MOI.VariableIndex(2)] + ci_1 = + primal_dual_map.primal_variable_data[MOI.VariableIndex( + 1, + )].dual_constraint + ci_2 = + primal_dual_map.primal_variable_data[MOI.VariableIndex( + 2, + )].dual_constraint @test MOI.get(dual_model, MOI.ConstraintName(), ci_1) == "dualcon_x1" @test MOI.get(dual_model, MOI.ConstraintName(), ci_2) == "dualcon_x2" end diff --git a/test/Tests/test_dualize_conic_linear.jl b/test/Tests/test_dualize_conic_linear.jl index e480e686..1ff782c9 100644 --- a/test/Tests/test_dualize_conic_linear.jl +++ b/test/Tests/test_dualize_conic_linear.jl @@ -55,7 +55,7 @@ @test MOI.get(dual_model, MOI.ConstraintSet(), ci) == MOI.Nonnegatives(3) - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec + primal_constraint_data = primal_dual_map.primal_constraint_data ci_zero = first( MOI.get( primal_model, @@ -65,7 +65,8 @@ }(), ), ) - @test primal_con_to_dual_var_vec[ci_zero] == MOI.VariableIndex.(1:2) + @test primal_constraint_data[ci_zero].dual_variables == + MOI.VariableIndex.(1:2) ci_nneg = first( MOI.get( primal_model, @@ -75,14 +76,19 @@ }(), ), ) - @test !haskey(primal_con_to_dual_var_vec, ci_nneg) - @test primal_dual_map.primal_convarcon_to_dual_con[ci_nneg] == ci + @test !haskey(primal_dual_map.primal_constraint_data, ci_nneg) + vis = primal_dual_map.primal_constrained_variables[ci_nneg] + for vi in vis + @test primal_dual_map.primal_variable_data[vi].dual_constraint == ci + end for i in 1:3 vi = MOI.VariableIndex(i) - @test !haskey(primal_dual_map.primal_var_to_dual_con, vi) - @test primal_dual_map.primal_convar_to_primal_convarcon_and_index[vi] == - (ci_nneg, i) + data = primal_dual_map.primal_variable_data[vi] + @test data.primal_constrained_variable_constraint == ci_nneg + @test data.primal_constrained_variable_index == i + @test data.dual_constraint == ci + @test data.dual_function === nothing end end @@ -188,14 +194,13 @@ @test MOI.coefficient.(eq_con3_fun.terms) == -[1.0] @test MOI.constant(eq_con3_fun) == [-4.0] - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec ci_zero = first( MOI.get( primal_model, MOI.ListOfConstraintIndices{MOI.VectorOfVariables,MOI.Zeros}(), ), ) - @test !haskey(primal_con_to_dual_var_vec, ci_zero) + @test !haskey(primal_dual_map.primal_constraint_data, ci_zero) ci_nneg = first( MOI.get( primal_model, @@ -205,8 +210,12 @@ }(), ), ) - @test !haskey(primal_con_to_dual_var_vec, ci_nneg) - @test primal_dual_map.primal_convarcon_to_dual_con[ci_nneg] == ci_nn + @test !haskey(primal_dual_map.primal_constraint_data, ci_nneg) + vis = primal_dual_map.primal_constrained_variables[ci_nneg] + for vi in vis + @test primal_dual_map.primal_variable_data[vi].dual_constraint == + ci_nn + end ci_npos = first( MOI.get( primal_model, @@ -216,8 +225,12 @@ }(), ), ) - @test !haskey(primal_con_to_dual_var_vec, ci_npos) - @test primal_dual_map.primal_convarcon_to_dual_con[ci_npos] == ci_np + @test !haskey(primal_dual_map.primal_constraint_data, ci_npos) + vis = primal_dual_map.primal_constrained_variables[ci_npos] + for vi in vis + @test primal_dual_map.primal_variable_data[vi].dual_constraint == + ci_np + end ci_aff_zero = first( MOI.get( primal_model, @@ -227,22 +240,32 @@ }(), ), ) - @test primal_con_to_dual_var_vec[ci_aff_zero] == MOI.VariableIndex.(1:3) + @test primal_dual_map.primal_constraint_data[ci_aff_zero].dual_variables == + MOI.VariableIndex.(1:3) - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test primal_var_to_dual_con[MOI.VariableIndex(1)] == ci_eq + data = primal_dual_map.primal_variable_data[MOI.VariableIndex(1)] + @test data.dual_constraint == ci_eq for i in 2:4 - vi = MOI.VariableIndex(i) - @test !haskey(primal_var_to_dual_con, vi) + data = primal_dual_map.primal_variable_data[MOI.VariableIndex(i)] + @test data.dual_constraint !== nothing end - @test primal_dual_map.primal_convar_to_primal_convarcon_and_index[MOI.VariableIndex( - 2, - )] == (ci_npos, 1) - @test primal_dual_map.primal_convar_to_primal_convarcon_and_index[MOI.VariableIndex( - 3, - )] == (ci_nneg, 1) - @test primal_dual_map.primal_convar_to_primal_convarcon_and_index[MOI.VariableIndex( - 4, - )] == (ci_zero, 1) + # @test primal_dual_map.primal_convar_to_primal_convarcon_and_index[MOI.VariableIndex( + # 2, + # )] == (ci_npos, 1) + # @test primal_dual_map.primal_convar_to_primal_convarcon_and_index[MOI.VariableIndex( + # 3, + # )] == (ci_nneg, 1) + # @test primal_dual_map.primal_convar_to_primal_convarcon_and_index[MOI.VariableIndex( + # 4, + # )] == (ci_zero, 1) + data = primal_dual_map.primal_variable_data[MOI.VariableIndex(2)] + @test data.primal_constrained_variable_constraint == ci_npos + @test data.primal_constrained_variable_index == 1 + data = primal_dual_map.primal_variable_data[MOI.VariableIndex(3)] + @test data.primal_constrained_variable_constraint == ci_nneg + @test data.primal_constrained_variable_index == 1 + data = primal_dual_map.primal_variable_data[MOI.VariableIndex(4)] + @test data.primal_constrained_variable_constraint == ci_zero + @test data.primal_constrained_variable_index == 1 end end diff --git a/test/Tests/test_dualize_exponential.jl b/test/Tests/test_dualize_exponential.jl index 0e8aee9f..5910efb3 100644 --- a/test/Tests/test_dualize_exponential.jl +++ b/test/Tests/test_dualize_exponential.jl @@ -125,19 +125,24 @@ ) @test dual_exp_con.variables == MOI.VariableIndex.(3:5) - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[eq_con1] == [MOI.VariableIndex(1)] - @test primal_con_to_dual_var_vec[eq_con2] == [MOI.VariableIndex(2)] - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[eq_con1].dual_variables == + [MOI.VariableIndex(1)] + @test primal_constraint_data[eq_con2].dual_variables == + [MOI.VariableIndex(2)] + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.VectorAffineFunction{Float64}, MOI.ExponentialCone, }( 1, - )] == MOI.VariableIndex.(3:5) + )].dual_variables == MOI.VariableIndex.(3:5) - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test primal_var_to_dual_con[MOI.VariableIndex(1)] == eq_con1 - @test primal_var_to_dual_con[MOI.VariableIndex(2)] == eq_con2 - @test primal_var_to_dual_con[MOI.VariableIndex(3)] == eq_con3 + primal_variable_data = primal_dual_map.primal_variable_data + @test primal_variable_data[MOI.VariableIndex(1)].dual_constraint == + eq_con1 + @test primal_variable_data[MOI.VariableIndex(2)].dual_constraint == + eq_con2 + @test primal_variable_data[MOI.VariableIndex(3)].dual_constraint == + eq_con3 end end diff --git a/test/Tests/test_dualize_power.jl b/test/Tests/test_dualize_power.jl index 2d79afd5..d13fa75a 100644 --- a/test/Tests/test_dualize_power.jl +++ b/test/Tests/test_dualize_power.jl @@ -125,13 +125,18 @@ dual_pow_con = MOI.get(dual_model, MOI.ConstraintFunction(), pow_con) - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[eq_con1] == [MOI.VariableIndex(1)] - @test primal_con_to_dual_var_vec[eq_con2] == [MOI.VariableIndex(2)] + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[eq_con1].dual_variables == + [MOI.VariableIndex(1)] + @test primal_constraint_data[eq_con2].dual_variables == + [MOI.VariableIndex(2)] - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test primal_var_to_dual_con[MOI.VariableIndex(1)] == eq_con1 - @test primal_var_to_dual_con[MOI.VariableIndex(2)] == eq_con2 - @test primal_var_to_dual_con[MOI.VariableIndex(3)] == eq_con3 + primal_variable_data = primal_dual_map.primal_variable_data + @test primal_variable_data[MOI.VariableIndex(1)].dual_constraint == + eq_con1 + @test primal_variable_data[MOI.VariableIndex(2)].dual_constraint == + eq_con2 + @test primal_variable_data[MOI.VariableIndex(3)].dual_constraint == + eq_con3 end end diff --git a/test/Tests/test_dualize_quadratic.jl b/test/Tests/test_dualize_quadratic.jl index ee6187ef..1e6fd7d4 100644 --- a/test/Tests/test_dualize_quadratic.jl +++ b/test/Tests/test_dualize_quadratic.jl @@ -124,36 +124,36 @@ @test MOI.constant.(eq_con3_fun) == 0.0 @test MOI.constant(eq_con3_set) == 0.0 - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, }( 1, - )] == [MOI.VariableIndex(1)] - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + )].dual_variables == [MOI.VariableIndex(1)] + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, }( 2, - )] == [MOI.VariableIndex(2)] + )].dual_variables == [MOI.VariableIndex(2)] - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test primal_var_to_dual_con[MOI.VariableIndex(1)] == + primal_variable_data = primal_dual_map.primal_variable_data + @test primal_variable_data[MOI.VariableIndex(1)].dual_constraint == MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 1, ) - @test primal_var_to_dual_con[MOI.VariableIndex(2)] == + @test primal_variable_data[MOI.VariableIndex(2)].dual_constraint == MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 2, ) - @test primal_var_to_dual_con[MOI.VariableIndex(3)] == + @test primal_variable_data[MOI.VariableIndex(3)].dual_constraint == MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, diff --git a/test/Tests/test_dualize_rsoc.jl b/test/Tests/test_dualize_rsoc.jl index 8f11a196..62c17f9f 100644 --- a/test/Tests/test_dualize_rsoc.jl +++ b/test/Tests/test_dualize_rsoc.jl @@ -125,16 +125,18 @@ ) @test rsoc_con.variables == MOI.VariableIndex.(1:4) - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone, }( 1, - )] == MOI.VariableIndex.(1:4) + )].dual_variables == MOI.VariableIndex.(1:4) - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test primal_var_to_dual_con[MOI.VariableIndex(1)] == eq_con1 - @test primal_var_to_dual_con[MOI.VariableIndex(2)] == eq_con2 + primal_variable_data = primal_dual_map.primal_variable_data + @test primal_variable_data[MOI.VariableIndex(1)].dual_constraint == + eq_con1 + @test primal_variable_data[MOI.VariableIndex(2)].dual_constraint == + eq_con2 end end diff --git a/test/Tests/test_dualize_sdp.jl b/test/Tests/test_dualize_sdp.jl index 0afb2f75..a588c555 100644 --- a/test/Tests/test_dualize_sdp.jl +++ b/test/Tests/test_dualize_sdp.jl @@ -125,18 +125,22 @@ sdp_con = MOI.get(dual_model, MOI.ConstraintFunction(), spd_con) - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[eq_con1] == [MOI.VariableIndex(1)] - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[eq_con1].dual_variables == + [MOI.VariableIndex(1)] + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.VectorAffineFunction{Float64}, MOI.PositiveSemidefiniteConeTriangle, }( 1, - )] == MOI.VariableIndex.(2:4) + )].dual_variables == MOI.VariableIndex.(2:4) - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test primal_var_to_dual_con[MOI.VariableIndex(1)] == eq_con1 - @test primal_var_to_dual_con[MOI.VariableIndex(2)] == eq_con2 - @test primal_var_to_dual_con[MOI.VariableIndex(3)] == eq_con3 + primal_variable_data = primal_dual_map.primal_variable_data + @test primal_variable_data[MOI.VariableIndex(1)].dual_constraint == + eq_con1 + @test primal_variable_data[MOI.VariableIndex(2)].dual_constraint == + eq_con2 + @test primal_variable_data[MOI.VariableIndex(3)].dual_constraint == + eq_con3 end end diff --git a/test/Tests/test_dualize_soc.jl b/test/Tests/test_dualize_soc.jl index 91b0129b..152c32b7 100644 --- a/test/Tests/test_dualize_soc.jl +++ b/test/Tests/test_dualize_soc.jl @@ -114,13 +114,13 @@ ) @test soc_con.variables == MOI.VariableIndex.(2:4) - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.VectorAffineFunction{Float64}, MOI.Zeros, }( 1, - )] == [MOI.VariableIndex(1)] + )].dual_variables == [MOI.VariableIndex(1)] primal_soc_con = first( MOI.get( primal_model, @@ -130,12 +130,15 @@ }(), ), ) - @test primal_con_to_dual_var_vec[primal_soc_con] == + @test primal_constraint_data[primal_soc_con].dual_variables == [MOI.VariableIndex(2); MOI.VariableIndex(3); MOI.VariableIndex(4)] - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test primal_var_to_dual_con[MOI.VariableIndex(1)] == eq_con1 - @test primal_var_to_dual_con[MOI.VariableIndex(2)] == eq_con2 - @test primal_var_to_dual_con[MOI.VariableIndex(3)] == eq_con3 + primal_variable_data = primal_dual_map.primal_variable_data + @test primal_variable_data[MOI.VariableIndex(1)].dual_constraint == + eq_con1 + @test primal_variable_data[MOI.VariableIndex(2)].dual_constraint == + eq_con2 + @test primal_variable_data[MOI.VariableIndex(3)].dual_constraint == + eq_con3 end end diff --git a/test/Tests/test_partial_dual_linear.jl b/test/Tests/test_partial_dual_linear.jl index b5302b13..20455cd0 100644 --- a/test/Tests/test_partial_dual_linear.jl +++ b/test/Tests/test_partial_dual_linear.jl @@ -81,16 +81,16 @@ @test MOI.constant.(eq_con1_fun) == 0.0 @test MOI.constant(eq_con1_set) == 0.0 - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}, }( 1, - )] == [MOI.VariableIndex(1)] + )].dual_variables == [MOI.VariableIndex(1)] - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test primal_var_to_dual_con[MOI.VariableIndex(1)] == + primal_variable_data = primal_dual_map.primal_variable_data + @test primal_variable_data[MOI.VariableIndex(1)].dual_constraint == MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, @@ -168,7 +168,7 @@ @test MOI.constant.(eq_con2_fun) == 0.0 @test MOI.constant(eq_con2_set) == 3.0 - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec + primal_constraint_data = primal_dual_map.primal_constraint_data vaf_npos, = MOI.get( primal_model, MOI.ListOfConstraintIndices{ @@ -176,19 +176,25 @@ MOI.Nonpositives, }(), ) - @test primal_con_to_dual_var_vec[vaf_npos] == + @test primal_constraint_data[vaf_npos].dual_variables == [MOI.VariableIndex(1); MOI.VariableIndex(2)] - vgt, = MOI.get( + vgt1, vgt2 = MOI.get( primal_model, MOI.ListOfConstraintIndices{ MOI.VariableIndex, MOI.GreaterThan{Float64}, }(), ) - @test primal_con_to_dual_var_vec[vgt] == [MOI.VariableIndex(3)] + @test primal_constraint_data[vgt1].dual_variables == + [MOI.VariableIndex(3)] - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test isempty(primal_var_to_dual_con) + for (vi, data) in primal_dual_map.primal_variable_data + vi = MOI.VariableIndex(2) + @test data.primal_constrained_variable_constraint == vgt2 + @test data.primal_constrained_variable_index == 0 + @test data.dual_constraint !== nothing + @test data.dual_function === nothing + end end @testset "lp12_test - x_1 and x_3 ignored" begin @@ -266,28 +272,28 @@ @test MOI.constant.(eq_con2_fun) == 0.0 @test MOI.constant(eq_con2_set) == 0.0 - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.VariableIndex, MOI.LessThan{Float64}, }( 2, - )] == [MOI.VariableIndex(3)] - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + )].dual_variables == [MOI.VariableIndex(3)] + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.VariableIndex, MOI.LessThan{Float64}, }( 1, - )] == [MOI.VariableIndex(2)] - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + )].dual_variables == [MOI.VariableIndex(2)] + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}, }( 1, - )] == [MOI.VariableIndex(1)] + )].dual_variables == [MOI.VariableIndex(1)] - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test primal_var_to_dual_con[MOI.VariableIndex(2)] == + primal_variable_data = primal_dual_map.primal_variable_data + @test primal_variable_data[MOI.VariableIndex(2)].dual_constraint == MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, diff --git a/test/Tests/test_partial_dual_quadratic.jl b/test/Tests/test_partial_dual_quadratic.jl index 6c47482c..96213456 100644 --- a/test/Tests/test_partial_dual_quadratic.jl +++ b/test/Tests/test_partial_dual_quadratic.jl @@ -105,29 +105,29 @@ @test MOI.constant.(eq_con2_fun) == 0.0 @test MOI.constant(eq_con2_set) == 0.0 - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, }( 1, - )] == [MOI.VariableIndex(1)] - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + )].dual_variables == [MOI.VariableIndex(1)] + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}, }( 2, - )] == [MOI.VariableIndex(2)] + )].dual_variables == [MOI.VariableIndex(2)] - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test primal_var_to_dual_con[MOI.VariableIndex(1)] == + primal_variable_data = primal_dual_map.primal_variable_data + @test primal_variable_data[MOI.VariableIndex(1)].dual_constraint == MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 1, ) - @test primal_var_to_dual_con[MOI.VariableIndex(2)] == + @test primal_variable_data[MOI.VariableIndex(2)].dual_constraint == MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, @@ -223,26 +223,37 @@ @test MOI.constant.(eq_con1_fun) == 0.0 @test MOI.constant(eq_con1_set) == -1.0 - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 1, - )] == [MOI.VariableIndex(1)] + )].dual_variables == [MOI.VariableIndex(1)] @test !haskey( - primal_con_to_dual_var_vec, + primal_constraint_data, MOI.ConstraintIndex{MOI.VariableIndex,MOI.GreaterThan{Float64}}(1), ) - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.VariableIndex, MOI.GreaterThan{Float64}, }( 2, - )] == [MOI.VariableIndex(2)] + )].dual_variables == [MOI.VariableIndex(2)] - primal_var_to_dual_con = primal_dual_map.primal_var_to_dual_con - @test isempty(primal_var_to_dual_con) + for (vi, data) in primal_dual_map.primal_variable_data + @test vi == MOI.VariableIndex(1) + @test data.primal_constrained_variable_constraint == + MOI.ConstraintIndex{ + MOI.VariableIndex, + MOI.GreaterThan{Float64}, + }( + 1, + ) + @test data.primal_constrained_variable_index == 0 + @test data.dual_constraint !== nothing + @test data.dual_function === nothing + end primal_parameter_to_dual_parameter = primal_dual_map.primal_parameter_to_dual_parameter @@ -330,25 +341,37 @@ @test MOI.constant.(eq_con1_fun) == 0.0 @test MOI.constant(eq_con1_set) == -1.0 - primal_con_to_dual_var_vec = primal_dual_map.primal_con_to_dual_var_vec - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + primal_constraint_data = primal_dual_map.primal_constraint_data + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}, }( 1, - )] == [MOI.VariableIndex(1)] + )].dual_variables == [MOI.VariableIndex(1)] @test !(haskey( - primal_con_to_dual_var_vec, + primal_constraint_data, MOI.ConstraintIndex{MOI.VariableIndex,MOI.GreaterThan{Float64}}(1), )) - @test primal_con_to_dual_var_vec[MOI.ConstraintIndex{ + @test primal_constraint_data[MOI.ConstraintIndex{ MOI.VariableIndex, MOI.GreaterThan{Float64}, }( 2, - )] == [MOI.VariableIndex(2)] + )].dual_variables == [MOI.VariableIndex(2)] - @test isempty(primal_dual_map.primal_var_to_dual_con) + for (vi, data) in primal_dual_map.primal_variable_data + @test vi == MOI.VariableIndex(1) + @test data.primal_constrained_variable_constraint == + MOI.ConstraintIndex{ + MOI.VariableIndex, + MOI.GreaterThan{Float64}, + }( + 1, + ) + @test data.primal_constrained_variable_index == 0 + @test data.dual_constraint !== nothing + @test data.dual_function === nothing + end primal_parameter_to_dual_parameter = primal_dual_map.primal_parameter_to_dual_parameter diff --git a/test/Tests/test_structures.jl b/test/Tests/test_structures.jl index be1a244d..2ccfd9d6 100644 --- a/test/Tests/test_structures.jl +++ b/test/Tests/test_structures.jl @@ -6,12 +6,6 @@ @testset "structures" begin primal_dual_map = Dualization.PrimalDualMap{Float64}() @test Dualization.is_empty(primal_dual_map) - push!( - primal_dual_map.primal_var_to_dual_con, - MOI.VariableIndex(1) => - MOI.ConstraintIndex{MOI.VariableIndex,MOI.EqualTo}(1), - ) - @test !Dualization.is_empty(primal_dual_map) # Constructors model = lp1_test()