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

[Utilities.CachingOptimizer] Enable fallback for ConstraintDual of variable indices #2373

Merged
merged 5 commits into from
Dec 21, 2023
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
37 changes: 27 additions & 10 deletions src/Utilities/cachingoptimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -915,6 +915,8 @@
return MOI.get(model.optimizer, attr)
end

_has_fallback(::MOI.AnyAttribute, ::Type{<:MOI.Index}) = false

function MOI.get(
model::CachingOptimizer,
attr::Union{MOI.AbstractVariableAttribute,MOI.AbstractConstraintAttribute},
Expand All @@ -924,6 +926,9 @@
return MOI.get(model.model_cache, attr, index)
end
_throw_if_get_attribute_not_allowed(model, attr; needs_optimizer_map = true)
if _has_fallback(attr, typeof(index))
return _get_fallback(model, attr, index)

Check warning on line 930 in src/Utilities/cachingoptimizer.jl

View check run for this annotation

Codecov / codecov/patch

src/Utilities/cachingoptimizer.jl#L930

Added line #L930 was not covered by tests
end
value = MOI.get(
model.optimizer,
attr,
Expand All @@ -935,12 +940,15 @@
function MOI.get(
model::CachingOptimizer,
attr::Union{MOI.AbstractVariableAttribute,MOI.AbstractConstraintAttribute},
indices::Vector{<:MOI.Index},
)
indices::Vector{I},
) where {I<:MOI.Index}
if !MOI.is_set_by_optimize(attr)
return MOI.get(model.model_cache, attr, indices)
end
_throw_if_get_attribute_not_allowed(model, attr; needs_optimizer_map = true)
if _has_fallback(attr, I)
return _get_fallback(model, attr, indices)

Check warning on line 950 in src/Utilities/cachingoptimizer.jl

View check run for this annotation

Codecov / codecov/patch

src/Utilities/cachingoptimizer.jl#L949-L950

Added lines #L949 - L950 were not covered by tests
end
value = MOI.get(
model.optimizer,
attr,
Expand All @@ -950,19 +958,29 @@
end

###
### MOI.ConstraintPrimal
### MOI.ConstraintPrimal and MOI.ConstraintDual
###

# ConstraintPrimal is slightly unique for CachingOptimizer because if the solver
# `ConstraintPrimal` is slightly unique for CachingOptimizer because if the solver
# doesn't support the attribute directly, we can use the fallback to query the
# function from the cache and the variable value from the optimizer.
# The `ConstraintDual` of a `VariableIndex` or `VectorOfVariables` can
# also be computed from the `ConstraintDual` of the other constraints.

function MOI.get(
_has_fallback(::MOI.ConstraintPrimal, ::Type{<:MOI.ConstraintIndex}) = true

Check warning on line 970 in src/Utilities/cachingoptimizer.jl

View check run for this annotation

Codecov / codecov/patch

src/Utilities/cachingoptimizer.jl#L970

Added line #L970 was not covered by tests

function _has_fallback(

Check warning on line 972 in src/Utilities/cachingoptimizer.jl

View check run for this annotation

Codecov / codecov/patch

src/Utilities/cachingoptimizer.jl#L972

Added line #L972 was not covered by tests
::MOI.ConstraintDual,
::Type{<:MOI.ConstraintIndex{F}},
) where {F<:Union{MOI.VariableIndex,MOI.VectorOfVariables}}
return true

Check warning on line 976 in src/Utilities/cachingoptimizer.jl

View check run for this annotation

Codecov / codecov/patch

src/Utilities/cachingoptimizer.jl#L976

Added line #L976 was not covered by tests
end

function _get_fallback(

Check warning on line 979 in src/Utilities/cachingoptimizer.jl

View check run for this annotation

Codecov / codecov/patch

src/Utilities/cachingoptimizer.jl#L979

Added line #L979 was not covered by tests
model::CachingOptimizer,
attr::MOI.ConstraintPrimal,
attr::MOI.AbstractConstraintAttribute,
index::MOI.ConstraintIndex,
)
_throw_if_get_attribute_not_allowed(model, attr; needs_optimizer_map = true)
try
return MOI.get(
model.optimizer,
Expand All @@ -978,12 +996,11 @@
end
end

function MOI.get(
function _get_fallback(

Check warning on line 999 in src/Utilities/cachingoptimizer.jl

View check run for this annotation

Codecov / codecov/patch

src/Utilities/cachingoptimizer.jl#L999

Added line #L999 was not covered by tests
model::CachingOptimizer,
attr::MOI.ConstraintPrimal,
attr::MOI.AbstractConstraintAttribute,
indices::Vector{<:MOI.ConstraintIndex},
)
_throw_if_get_attribute_not_allowed(model, attr; needs_optimizer_map = true)
try
return MOI.get(
model.optimizer,
Expand Down
47 changes: 38 additions & 9 deletions test/Utilities/cachingoptimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -851,14 +851,6 @@ function MOI.get(
return 1.2
end

function MOI.get(
::_GetFallbackModel1310,
::MOI.ConstraintDual,
::MOI.ConstraintIndex,
)
return 1.2
end

function test_ConstraintPrimal_fallback()
model = MOI.Utilities.CachingOptimizer(
MOI.Utilities.Model{Float64}(),
Expand All @@ -880,6 +872,43 @@ function test_ConstraintPrimal_fallback()
return
end

function test_ConstraintDual_variable_fallback()
model = MOI.Utilities.CachingOptimizer(
MOI.Utilities.Model{Float64}(),
_GetFallbackModel1310(),
)
x = MOI.add_variable(model)
cx = MOI.add_constraint(model, x, MOI.GreaterThan(1.0))
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
MOI.set(model, MOI.ObjectiveFunction{MOI.VariableIndex}(), x)
MOI.optimize!(model)
@test MOI.get(model, MOI.ConstraintDual(), cx) == 1.0
@test_throws(
MOI.ResultIndexBoundsError(MOI.ConstraintDual(2), 1),
MOI.get(model, MOI.ConstraintDual(2), cx),
)
@test_throws(
MOI.ResultIndexBoundsError(MOI.ConstraintDual(2), 1),
MOI.get(model, MOI.ConstraintDual(2), [cx]),
)
return
end

function test_ConstraintDual_nonvariable_nofallback()
model = MOI.Utilities.CachingOptimizer(
MOI.Utilities.Model{Float64}(),
_GetFallbackModel1310(),
)
x = MOI.add_variable(model)
cx = MOI.add_constraint(model, x + 1.0, MOI.GreaterThan(1.0))
MOI.optimize!(model)
@test_throws(
MOI.GetAttributeNotAllowed,
MOI.get(model, MOI.ConstraintDual(), cx),
)
return
end

function test_ConstraintPrimal_fallback_error()
model = MOI.Utilities.CachingOptimizer(
MOI.Utilities.Model{Float64}(),
Expand Down Expand Up @@ -926,7 +955,7 @@ function test_DualObjectiveValue_fallback()
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
MOI.set(model, MOI.ObjectiveFunction{typeof(x)}(), x)
MOI.optimize!(model)
@test MOI.get(model, MOI.DualObjectiveValue()) == 1.2
@test MOI.get(model, MOI.DualObjectiveValue()) == 1.0
@test_throws(
MOI.ResultIndexBoundsError(MOI.DualObjectiveValue(2), 1),
MOI.get(model, MOI.DualObjectiveValue(2)),
Expand Down
Loading