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
52 changes: 37 additions & 15 deletions src/Bridges/bridge_optimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1652,27 +1652,49 @@ end

function MOI.get(
b::AbstractBridgeOptimizer,
attr::MOI.ConstraintPrimal,
attr::Union{MOI.ConstraintPrimal,MOI.ConstraintPrimalStart},
ci::MOI.ConstraintIndex{F,S},
) where {F<:MOI.AbstractScalarFunction,S<:MOI.AbstractScalarSet}
if is_bridged(b, ci)
MOI.throw_if_not_valid(b, ci)
return call_in_context(MOI.get, b, ci, attr)
end
if Variable.has_bridges(Variable.bridges(b))
# In this case, the scalar constraint might contain bridged variables
# that include constants, like `x >= 1` being mapped to `y >= 0`,
# `x := y + 1`. If this is true, then `normalize_and_add_constraint`
# may move the constants into the set, and querying `ConstraintPrimal`
# from `b.model` will return the value of `y`, not `y + 1`. As a
# work-around, we use `get_fallback`, which first queries
# `ConstraintFunction` and then evaluates the function at the point
# defined by `VariablePrimal`. Querying `ConstraintFunction` accounts
# for the case where constants were moved to the set, so we return the
# correct value.
return MOI.Utilities.get_fallback(b, attr, ci)
end
return MOI.get(b.model, attr, ci)
ret = MOI.get(b.model, attr, ci)
if !Variable.has_bridges(Variable.bridges(b)) || ret === nothing
return ret
end
# In this case, the scalar constraint might contain bridged variables
# that include constants, like `x >= 1` being mapped to `y >= 0`,
# `x := y + 1`. If this is true, then `normalize_and_add_constraint` may
# move the constants into the set, so we need to convert the primal value
# from `y`-space to `x`-space by adding back the constant offset.
f = MOI.get(b, MOI.ConstraintFunction(), ci)
offset = MOI.constant(bridged_function(b, f), eltype(ret))
return ret .+ offset
end

function MOI.set(
b::AbstractBridgeOptimizer,
attr::MOI.ConstraintPrimalStart,
ci::MOI.ConstraintIndex{F,S},
value,
) where {F<:MOI.AbstractScalarFunction,S<:MOI.AbstractScalarSet}
if is_bridged(b, ci)
MOI.throw_if_not_valid(b, ci)
return call_in_context(MOI.set, b, ci, attr, value)
elseif !Variable.has_bridges(Variable.bridges(b)) || value === nothing
MOI.set(b.model, attr, ci, value)
return
end
# In this case, the scalar constraint might contain bridged variables
# that include constants, like `x >= 1` being mapped to `y >= 0`,
# `x := y + 1`. If this is true, then `normalize_and_add_constraint` may
# move the constants into the set, so we need to convert the primal start
# from `x`-space to `y`-space by subtracting the constant offset.
f = MOI.get(b, MOI.ConstraintFunction(), ci)
offset = MOI.constant(bridged_function(b, f), eltype(value))
MOI.set(b.model, attr, ci, value - offset)
return
end

function MOI.supports(
Expand Down
13 changes: 13 additions & 0 deletions test/Bridges/Constraint/test_VectorizeBridge.jl
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,19 @@ function test_constraint_primal_ray()
return
end

function test_constraint_primal_start()
mock = MOI.Utilities.MockOptimizer(
MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
)
model = MOI.Bridges.Constraint.Vectorize{Float64}(mock)
x, c = MOI.add_constrained_variables(model, MOI.Nonnegatives(2))
ci = MOI.add_constraint(model, 1.0 * x[1] + x[2], MOI.EqualTo(1.0))
@test MOI.supports(model, MOI.ConstraintPrimalStart(), typeof(ci))
MOI.set(model, MOI.ConstraintPrimalStart(), ci, 1.0)
@test MOI.get(model, MOI.ConstraintPrimalStart(), ci) == 1.0
return
end

end # module

TestConstraintVectorize.runtests()
20 changes: 20 additions & 0 deletions test/Bridges/Variable/test_VectorizeBridge.jl
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,26 @@ function test_variable_primal_ray()
return
end

function test_constraint_primal_start_offset()
inner = MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}())
model = MOI.Bridges.Variable.Vectorize{Float64}(inner)
x, cx = MOI.add_constrained_variable(model, MOI.GreaterThan(3.0))
ci = MOI.add_constraint(model, 1.0 * x, MOI.LessThan(10.0))
MOI.set(model, MOI.ConstraintPrimalStart(), ci, 7.0)
@test MOI.get(model, MOI.ConstraintPrimalStart(), ci) == 7.0
# Outer model is:
# x >= 3
# 1.0 * x <= 10 [primal_start = 7]
# Inner model is:
# y >= 0 x := y + 3
# 1.0 * y <= 7.0 [primal_start = 4]
# x = y + 3, y >= 0
F, S = MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}
ci_inner = only(MOI.get(inner, MOI.ListOfConstraintIndices{F,S}()))
@test MOI.get(inner, MOI.ConstraintPrimalStart(), ci_inner) == 4.0
return
end

end # module

TestVariableVectorize.runtests()
Loading