Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/constrained_variables.jl
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,16 @@ function _select_constrained_variables(
end
return
end

function _get_parameter_variables(::PrimalDualMap{T}, primal_model) where {T}
cis = MOI.get(
primal_model,
MOI.ListOfConstraintIndices{MOI.VariableIndex,MOI.Parameter{T}}(),
)
parameters = Set{MOI.VariableIndex}()
for ci in cis
vi = MOI.get(primal_model, MOI.ConstraintFunction(), ci)
push!(parameters, vi)
end
return parameters
end
9 changes: 9 additions & 0 deletions src/dual_equality_constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,15 @@ function _fill_scalar_affine_terms!(
return
end

function _fill_scalar_affine_terms!(
::Dict{MOI.VariableIndex,Vector{MOI.ScalarAffineTerm{T}}},
primal_constraint_data,
::MOI.ModelLike,
::MOI.ConstraintIndex{MOI.VariableIndex,MOI.Parameter{T}},
) where {T}
return
end

function _fill_scalar_affine_terms!(
scalar_affine_terms::Dict{
MOI.VariableIndex,
Expand Down
20 changes: 16 additions & 4 deletions src/dual_model_variables.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ function _add_dual_vars_in_dual_cones(
primal_dual_map::PrimalDualMap{T},
dual_names::DualNames,
con_types::Vector{Tuple{Type,Type}},
variable_parameters,
) where {T}
dual_obj_affine_terms = Dict{MOI.VariableIndex,T}()
parameters_set = Set(variable_parameters)
for (F, S) in con_types
_add_dual_vars_in_dual_cones(
dual_obj_affine_terms,
Expand All @@ -21,6 +23,7 @@ function _add_dual_vars_in_dual_cones(
dual_names,
F,
S,
parameters_set,
)
end
return dual_obj_affine_terms
Expand All @@ -34,13 +37,22 @@ function _add_dual_vars_in_dual_cones(
dual_names::DualNames,
::Type{F},
::Type{S},
parameters_set,
) 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 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
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
(
F <: MOI.VariableIndex &&
MOI.get(primal_model, MOI.ConstraintFunction(), ci) in
parameters_set
)
# if a parameter is constrained, either because the set is
# Parameter or because it was user defined parameter its constraint
# will lead to a useless dual variable.
continue
end
# Add dual variable to dual cone
Expand Down
14 changes: 13 additions & 1 deletion src/dualize.jl
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function dualize(
primal_model::MOI.ModelLike,
dual_problem::DualProblem{T},
dual_names::DualNames,
variable_parameters::Vector{MOI.VariableIndex},
_variable_parameters::Vector{MOI.VariableIndex},
ignore_objective::Bool,
consider_constrained_variables::Bool,
) where {T}
Expand All @@ -80,6 +80,15 @@ function dualize(
con_types = MOI.get(primal_model, MOI.ListOfConstraintTypesPresent())
supported_constraints(con_types) # Errors if constraint cant be dualized

# merge user listed parameters and MOI.VariableIndex-in-MOI.Parameter{T}
moi_parameters =
_get_parameter_variables(dual_problem.primal_dual_map, primal_model)
all_parameters = Set{MOI.VariableIndex}(_variable_parameters)
for p in moi_parameters
push!(all_parameters, p)
end
variable_parameters = collect(all_parameters)

# Set the dual model objective sense
_set_dual_model_sense(dual_problem.dual_model, primal_model)

Expand All @@ -98,6 +107,8 @@ function dualize(
# the respective primal variable will not be a constrained variable (with
# respect to that constraint).
# Constrained variables are registered in `primal_constrained_variables`.
# Since MOI.VariableIndex-in-MOI.Parameter{T} are in the variable_parameters
# list, they are not considered constrained variables, they are parameters.
if consider_constrained_variables
_select_constrained_variables(
dual_problem,
Expand Down Expand Up @@ -132,6 +143,7 @@ function dualize(
dual_problem.primal_dual_map,
dual_names,
con_types,
variable_parameters,
)

# Creates variables in the dual problem that represent parameters in the
Expand Down
7 changes: 7 additions & 0 deletions src/supported.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ end

supported_constraint(::Type, ::Type) = false

function supported_constraint(
::Type{MOI.VariableIndex},
S::Type{MOI.Parameter{T}},
) where {T}
return true
end

function supported_constraint(
::Type{<:Union{MOI.VariableIndex,MOI.ScalarAffineFunction}},
S::Type{<:MOI.AbstractScalarSet},
Expand Down
128 changes: 115 additions & 13 deletions test/Tests/test_partial_dual_linear.jl
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,13 @@
s.t.
2x1 + x2 - 3 <= 0 :y_2
x1 + 2x2 - 3 <= 0 :y_3
x1 >= 1 :y_1
x1 >= 1 :no dual here
x2 >= 0
ignore x_1 during dualization
dual
obj ignored
s.t.
-y_2 - 2y_3 >= 3 :x_2
y_1 >= 0
y_2 <= 0
y_3 <= 0
=#
Expand All @@ -126,11 +125,10 @@
dual_model = dual.dual_model
primal_dual_map = dual.primal_dual_map

@test MOI.get(dual_model, MOI.NumberOfVariables()) == 3
@test MOI.get(dual_model, MOI.NumberOfVariables()) == 2
list_of_cons = MOI.get(dual_model, MOI.ListOfConstraintTypesPresent())
@test Set(list_of_cons) == Set(
[
(MOI.VariableIndex, MOI.GreaterThan{Float64})
(MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64})
(MOI.VectorOfVariables, MOI.Nonpositives)
],
Expand All @@ -141,7 +139,7 @@
MOI.VariableIndex,
MOI.GreaterThan{Float64},
}(),
) == 1
) == 0
@test MOI.get(
dual_model,
MOI.NumberOfConstraints{MOI.VectorOfVariables,MOI.Nonpositives}(),
Expand Down Expand Up @@ -185,8 +183,7 @@
MOI.GreaterThan{Float64},
}(),
)
@test primal_constraint_data[vgt1].dual_variables ==
[MOI.VariableIndex(3)]
@test !haskey(primal_constraint_data, vgt1) # as this was not dualized

for (vi, data) in primal_dual_map.primal_variable_data
vi = MOI.VariableIndex(2)
Expand All @@ -203,7 +200,7 @@
min 4x_3 + 5
s.t.
x_1 + 2x_2 + x_3 <= 20 :y_3
x_1 <= 1 :y_1
x_1 <= 1 : # no dual here: y_1
x_2 <= 3 :y_2
ignoring x_1 and x_3
dual
Expand All @@ -212,7 +209,7 @@
# y_1 + y_3 == 0 :x_1
y_2 + 2y_3 == 0 :x_2
# y_3 == 4.0 :x_3
y_1 <= 0
# y_1 <= 0
y_2 <= 0
y_3 <= 0
=#
Expand All @@ -228,7 +225,7 @@
dual_model = dual.dual_model
primal_dual_map = dual.primal_dual_map

@test MOI.get(dual_model, MOI.NumberOfVariables()) == 3
@test MOI.get(dual_model, MOI.NumberOfVariables()) == 2
list_of_cons = MOI.get(dual_model, MOI.ListOfConstraintTypesPresent())
@test Set(list_of_cons) == Set(
[
Expand All @@ -239,7 +236,7 @@
@test MOI.get(
dual_model,
MOI.NumberOfConstraints{MOI.VariableIndex,MOI.LessThan{Float64}}(),
) == 3
) == 2
@test MOI.get(
dual_model,
MOI.NumberOfConstraints{
Expand Down Expand Up @@ -278,13 +275,118 @@
MOI.LessThan{Float64},
}(
2,
)].dual_variables == [MOI.VariableIndex(3)]
)].dual_variables == [MOI.VariableIndex(2)]
@test !haskey(
primal_constraint_data,
MOI.ConstraintIndex{MOI.VariableIndex,MOI.LessThan{Float64}}(1),
)
@test primal_constraint_data[MOI.ConstraintIndex{
MOI.VariableIndex,
MOI.ScalarAffineFunction{Float64},
MOI.LessThan{Float64},
}(
1,
)].dual_variables == [MOI.VariableIndex(1)]

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},
}(
1,
)
end

@testset "lp12_test - x_1 ignored x_3 moi_param" begin
#=
primal
min 4x_3 + 5
s.t.
x_1 + 2x_2 + x_3 <= 20 :y_3
x_1 <= 1 : # no dual here: y_1
x_2 <= 3 :y_2
ignoring x_1 and x_3
dual
obj ignored
s.t.
# y_1 + y_3 == 0 :x_1
y_2 + 2y_3 == 0 :x_2
# y_3 == 4.0 :x_3
# y_1 <= 0
y_2 <= 0
y_3 <= 0
=#
primal_model = lp12_test()
MOI.add_constraint(
primal_model,
MOI.VariableIndex(3),
MOI.Parameter{Float64}(0.0),
)
dual = Dualization.dualize(
primal_model,
variable_parameters = MOI.VariableIndex[MOI.VariableIndex(1),
# MOI.VariableIndex(3), # as a param
],
ignore_objective = true,
)
dual_model = dual.dual_model
primal_dual_map = dual.primal_dual_map

@test MOI.get(dual_model, MOI.NumberOfVariables()) == 2
list_of_cons = MOI.get(dual_model, MOI.ListOfConstraintTypesPresent())
@test Set(list_of_cons) == Set(
[
(MOI.VariableIndex, MOI.LessThan{Float64})
(MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64})
],
)
@test MOI.get(
dual_model,
MOI.NumberOfConstraints{MOI.VariableIndex,MOI.LessThan{Float64}}(),
) == 2
@test MOI.get(
dual_model,
MOI.NumberOfConstraints{
MOI.ScalarAffineFunction{Float64},
MOI.EqualTo{Float64},
}(),
) == 1

eq_con2_fun = MOI.get(
dual_model,
MOI.ConstraintFunction(),
MOI.ConstraintIndex{
MOI.ScalarAffineFunction{Float64},
MOI.EqualTo{Float64},
}(
1,
),
)
eq_con2_set = MOI.get(
dual_model,
MOI.ConstraintSet(),
MOI.ConstraintIndex{
MOI.ScalarAffineFunction{Float64},
MOI.EqualTo{Float64},
}(
1,
),
)
@test MOI.coefficient.(eq_con2_fun.terms) == [2.0; 1.0]
@test MOI.constant.(eq_con2_fun) == 0.0
@test MOI.constant(eq_con2_set) == 0.0

primal_constraint_data = primal_dual_map.primal_constraint_data
@test primal_constraint_data[MOI.ConstraintIndex{
MOI.VariableIndex,
MOI.LessThan{Float64},
}(
2,
)].dual_variables == [MOI.VariableIndex(2)]
@test !haskey(
primal_constraint_data,
MOI.ConstraintIndex{MOI.VariableIndex,MOI.LessThan{Float64}}(1),
)
@test primal_constraint_data[MOI.ConstraintIndex{
MOI.ScalarAffineFunction{Float64},
MOI.LessThan{Float64},
Expand Down
Loading
Loading