Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for starting values #166

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
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
186 changes: 160 additions & 26 deletions src/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,11 @@ end
function MOI.copy_to(dest::DualOptimizer, src::MOI.ModelLike)
dualize(src, dest.dual_problem)
idx_map = MOI.Utilities.IndexMap()
for vi in MOI.get(src, MOI.ListOfVariableIndices())
vis_src = MOI.get(src, MOI.ListOfVariableIndices())
for vi in vis_src
setindex!(idx_map, vi, vi)
end
MOI.Utilities.pass_attributes(dest, src, idx_map, vis_src)
for (F, S) in MOI.get(src, MOI.ListOfConstraintTypesPresent())
for con in MOI.get(src, MOI.ListOfConstraintIndices{F,S}())
setindex!(idx_map, con, con)
Expand Down Expand Up @@ -319,9 +321,68 @@ function _get_at_index(
return _get(optimizer, attr, ci_primal, ci_dual)[idx]
end

_minus(::Nothing) = nothing
_minus(x) = -x

function _dual_attribute(attr::Union{MOI.VariablePrimal,MOI.ConstraintPrimal})
return MOI.ConstraintDual(attr.result_index)
end

function _dual_attribute(
::Union{MOI.VariablePrimalStart,MOI.ConstraintPrimalStart},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know know that it makes sense to support MOI.ConstraintPrimalStart?

)
return MOI.ConstraintDualStart()
end

function _dual_attribute(attr::MOI.ConstraintDual)
return MOI.ConstraintPrimal(attr.result_index)
end

function _dual_attribute(::MOI.ConstraintDualStart)
return MOI.ConstraintPrimalStart()
end

function _variable_dual_attribute(attr::MOI.ConstraintDual)
return MOI.VariablePrimal(attr.result_index)
end

function _variable_dual_attribute(::MOI.ConstraintDualStart)
return MOI.VariablePrimalStart()
end

function MOI.supports(
::DualOptimizer,
::MOI.VariablePrimalStart,
::Type{MOI.VariableIndex},
)
return true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to check whether the inner optimizer supports ConstraintDualStart

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit tricky because we don't know the type of constraints. The inner may not support equality constraints in which case we cannot even ask for whether the solver supports ScalarAffineFunction-in-EqualTo

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I don't know what a good answer is here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I have an idea that I'll try next week

end

function MOI.set(
optimizer::DualOptimizer,
attr::MOI.VariablePrimalStart,
vi::MOI.VariableIndex,
value,
)
primal_dual_map = optimizer.dual_problem.primal_dual_map
if vi in keys(primal_dual_map.constrained_var_idx)
error(
"Setting starting value for variables constrained at creation is not supported yet",
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should throw MOI.SetAttributeNotAllowed{MOI.VariablePrimalStart}

else
MOI.set(
optimizer.dual_problem.dual_model,
_dual_attribute(attr),
get_ci_dual_problem(optimizer, vi),
_minus(value),
)
end
return
end

function MOI.get(
optimizer::DualOptimizer,
::MOI.VariablePrimal,
attr::Union{MOI.VariablePrimal,MOI.VariablePrimalStart},
vi::MOI.VariableIndex,
)
primal_dual_map = optimizer.dual_problem.primal_dual_map
Expand All @@ -330,23 +391,57 @@ function MOI.get(
ci_dual = primal_dual_map.constrained_var_dual[ci_primal]
return _get_at_index(
optimizer,
MOI.ConstraintDual(),
_dual_attribute(attr),
ci_primal,
ci_dual,
idx,
)
else
return -MOI.get(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The - does not account for VariablePrimalStart returning nothing

optimizer.dual_problem.dual_model,
MOI.ConstraintDual(),
_dual_attribute(attr),
get_ci_dual_problem(optimizer, vi),
)
end
end

function MOI.supports(
optimizer::DualOptimizer,
attr::MOI.ConstraintDualStart,
::Type{<:MOI.ConstraintIndex},
)
return MOI.supports(
optimizer.dual_problem.dual_model,
_variable_dual_attribute(attr),
MOI.VariableIndex,
)
end

function MOI.set(
optimizer::DualOptimizer,
attr::MOI.ConstraintDualStart,
ci::MOI.ConstraintIndex,
value,
)
primal_dual_map = optimizer.dual_problem.primal_dual_map
if ci in keys(primal_dual_map.constrained_var_dual)
error(
"Setting starting value for variables constrained at creation is not supported yet",
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SetAttributeNotAllowed

else
MOI.set(
optimizer.dual_problem.dual_model,
_variable_dual_attribute(attr),
get_vi_dual_problem(optimizer, ci),
value,
)
end
return
end

function MOI.get(
optimizer::DualOptimizer,
attr::MOI.ConstraintDual,
attr::Union{MOI.ConstraintDual,MOI.ConstraintDualStart},
ci::MOI.ConstraintIndex{F,S},
) where {F<:MOI.AbstractScalarFunction,S<:MOI.AbstractScalarSet}
primal_dual_map = optimizer.dual_problem.primal_dual_map
Expand All @@ -358,7 +453,7 @@ function MOI.get(
) do vi
return MOI.get(
optimizer.dual_problem.dual_model,
MOI.VariablePrimal(),
_variable_dual_attribute(attr),
vi,
)
end
Expand All @@ -370,21 +465,21 @@ function MOI.get(
)
return MOI.get(
optimizer.dual_problem.dual_model,
MOI.ConstraintPrimal(),
_dual_attribute(attr),
ci_dual,
) - MOI.constant(set)
else
return MOI.get(
optimizer.dual_problem.dual_model,
MOI.VariablePrimal(),
_variable_dual_attribute(attr),
get_vi_dual_problem(optimizer, ci),
)
end
end

function MOI.get(
optimizer::DualOptimizer,
::MOI.ConstraintDual,
attr::Union{MOI.ConstraintDual,MOI.ConstraintDualStart},
ci::MOI.ConstraintIndex{F,S},
) where {F<:MOI.AbstractVectorFunction,S<:MOI.AbstractVectorSet}
primal_dual_map = optimizer.dual_problem.primal_dual_map
Expand All @@ -396,35 +491,74 @@ function MOI.get(
) do vi
return MOI.get(
optimizer.dual_problem.dual_model,
MOI.VariablePrimal(),
_variable_dual_attribute(attr),
vi,
)
end
end
return MOI.get(
optimizer.dual_problem.dual_model,
MOI.ConstraintPrimal(),
_dual_attribute(attr),
primal_dual_map.constrained_var_dual[ci],
)
else
return MOI.get.(
optimizer.dual_problem.dual_model,
MOI.VariablePrimal(),
_variable_dual_attribute(attr),
get_vis_dual_problem(optimizer, ci),
)
end
end

function MOI.supports(
::DualOptimizer,
attr::MOI.ConstraintPrimalStart,
C::Type{<:MOI.ConstraintIndex},
)
return MOI.supports(
optimizer.dual_problem.dual_model,
_dual_attribute(attr),
C,
)
end

function MOI.set(
optimizer::DualOptimizer,
attr::MOI.ConstraintPrimalStart,
ci::MOI.ConstraintIndex{F},
value,
) where {F<:MOI.AbstractScalarFunction}
primal_dual_map = optimizer.dual_problem.primal_dual_map
if ci in keys(primal_dual_map.constrained_var_dual)
error(
"Setting starting value for variables constrained at creation is not supported yet",
)
elseif haskey(primal_dual_map.primal_con_dual_con, ci)
# If it has no key then there is no dual constraint
ci_dual_problem = get_ci_dual_problem(optimizer, ci)
if !isnothing(value) && (F <: MOI.AbstractScalarFunction)
value -= get_primal_ci_constant(optimizer, ci)
end
MOI.set(
optimizer.dual_problem.dual_model,
_dual_attribute(attr),
ci_dual_problem,
value,
)
end
return
end

function MOI.get(
optimizer::DualOptimizer,
::MOI.ConstraintPrimal,
attr::Union{MOI.ConstraintPrimal,MOI.ConstraintPrimalStart},
ci::MOI.ConstraintIndex{F,S},
) where {F<:MOI.AbstractScalarFunction,S<:MOI.AbstractScalarSet}
primal_dual_map = optimizer.dual_problem.primal_dual_map
if ci in keys(primal_dual_map.constrained_var_dual)
return _get(
optimizer,
MOI.ConstraintDual(),
_dual_attribute(attr),
ci,
primal_dual_map.constrained_var_dual[ci],
)
Expand All @@ -437,22 +571,22 @@ function MOI.get(
ci_dual_problem = get_ci_dual_problem(optimizer, ci)
return MOI.get(
optimizer.dual_problem.dual_model,
MOI.ConstraintDual(),
_dual_attribute(attr),
ci_dual_problem,
) - primal_ci_constant
end
end

function MOI.get(
optimizer::DualOptimizer{T},
::MOI.ConstraintPrimal,
attr::Union{MOI.ConstraintPrimal,MOI.ConstraintPrimalStart},
ci::MOI.ConstraintIndex{F,S},
) where {T,F<:MOI.AbstractVectorFunction,S<:MOI.AbstractVectorSet}
primal_dual_map = optimizer.dual_problem.primal_dual_map
if ci in keys(primal_dual_map.constrained_var_dual)
return _get(
optimizer,
MOI.ConstraintDual(),
_dual_attribute(attr),
ci,
primal_dual_map.constrained_var_dual[ci],
)
Expand All @@ -466,7 +600,7 @@ function MOI.get(
ci_dual_problem = get_ci_dual_problem(optimizer, ci)
return MOI.get(
optimizer.dual_problem.dual_model,
MOI.ConstraintDual(),
_dual_attribute(attr),
ci_dual_problem,
)
end
Expand All @@ -491,20 +625,20 @@ function dual_status(term::MOI.TerminationStatusCode)
return term
end

function MOI.get(optimizer::DualOptimizer, ::MOI.ObjectiveValue)
return MOI.get(optimizer.dual_problem.dual_model, MOI.DualObjectiveValue())
function MOI.get(optimizer::DualOptimizer, attr::MOI.ObjectiveValue)
return MOI.get(optimizer.dual_problem.dual_model, MOI.DualObjectiveValue(attr.result_index))
end

function MOI.get(optimizer::DualOptimizer, ::MOI.DualObjectiveValue)
return MOI.get(optimizer.dual_problem.dual_model, MOI.ObjectiveValue())
function MOI.get(optimizer::DualOptimizer, attr::MOI.DualObjectiveValue)
return MOI.get(optimizer.dual_problem.dual_model, MOI.ObjectiveValue(attr.result_index))
end

function MOI.get(optimizer::DualOptimizer, ::MOI.PrimalStatus)
return MOI.get(optimizer.dual_problem.dual_model, MOI.DualStatus())
function MOI.get(optimizer::DualOptimizer, attr::MOI.PrimalStatus)
return MOI.get(optimizer.dual_problem.dual_model, MOI.DualStatus(attr.result_index))
end

function MOI.get(optimizer::DualOptimizer, ::MOI.DualStatus)
return MOI.get(optimizer.dual_problem.dual_model, MOI.PrimalStatus())
function MOI.get(optimizer::DualOptimizer, attr::MOI.DualStatus)
return MOI.get(optimizer.dual_problem.dual_model, MOI.PrimalStatus(attr.result_index))
end

function MOI.set(
Expand Down
14 changes: 14 additions & 0 deletions test/Tests/test_MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,18 @@
DualOptimizer{Float32,Caching_OptimizerType}
end
end

@testset "Start" begin
model = MOI.Utilities.UniversalFallback(TestModel{Float64}())
x = MOI.add_variable(model)
c = MOI.add_constraint(model, 2.0 * x, MOI.GreaterThan(0.0))
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
MOI.set(model, MOI.VariablePrimalStart(), x, 1.0)
MOI.set(model, MOI.ConstraintPrimalStart(), c, 3.0)
MOI.set(model, MOI.ConstraintDualStart(), c, 4.0)
dual_problem = Dualization.DualProblem{Float64}(TestModel{Float64}())
OptimizerType = typeof(dual_problem.dual_model)
dual = DualOptimizer{Float64,OptimizerType}(dual_problem)
index_map = MOI.copy_to(dual, model)
end
end
Loading