Skip to content

Commit

Permalink
Allow SetMapBridge to use bridge value (#2509)
Browse files Browse the repository at this point in the history
  • Loading branch information
blegat authored Jun 22, 2024
1 parent ad45651 commit 85a1db5
Show file tree
Hide file tree
Showing 4 changed files with 313 additions and 35 deletions.
1 change: 1 addition & 0 deletions docs/src/submodules/Bridges/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ Bridges.debug_supports
## [SetMap API](@id constraint_set_map)

```@docs
Bridges.MapNotInvertible
Bridges.map_set
Bridges.inverse_map_set
Bridges.map_function
Expand Down
49 changes: 42 additions & 7 deletions src/Bridges/Constraint/set_map.jl
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,29 @@ end

# Attributes, Bridge acting as a constraint

# MapNotInvertible is thrown if the bridge does not support inverting the
# function. The user doesn't need to know this, only that they cannot get the
# attribute. Throwing `GetAttributeNotAllowed` allows `CachingOptimizer` to fall
# back to using the cache.
function _not_invertible_error_message(attr, message)
return "Cannot get `$attr` as the constraint is reformulated through a linear transformation that is not invertible. $message"
end

function MOI.get(
model::MOI.ModelLike,
attr::MOI.ConstraintFunction,
bridge::MultiSetMapBridge{T,S1,G},
) where {T,S1,G}
mapped_func = MOI.get(model, attr, bridge.constraint)
func = MOI.Bridges.inverse_map_function(typeof(bridge), mapped_func)
func = try
MOI.Bridges.inverse_map_function(bridge, mapped_func)
catch err
if err isa MOI.Bridges.MapNotInvertible
msg = _not_invertible_error_message(attr, err.message)
throw(MOI.GetAttributeNotAllowed(attr, msg))
end
rethrow(err)
end
return MOI.Utilities.convert_approx(G, func)
end

Expand All @@ -123,7 +139,7 @@ function MOI.get(
bridge::MultiSetMapBridge,
)
set = MOI.get(model, attr, bridge.constraint)
return MOI.Bridges.inverse_map_set(typeof(bridge), set)
return MOI.Bridges.inverse_map_set(bridge, set)
end

function MOI.set(
Expand All @@ -132,7 +148,7 @@ function MOI.set(
bridge::MultiSetMapBridge{T,S1},
set::S1,
) where {T,S1}
new_set = MOI.Bridges.map_set(typeof(bridge), set)
new_set = MOI.Bridges.map_set(bridge, set)
MOI.set(model, attr, bridge.constraint, new_set)
return
end
Expand All @@ -146,7 +162,18 @@ function MOI.get(
if value === nothing
return nothing
end
return MOI.Bridges.inverse_map_function(typeof(bridge), value)
try
return MOI.Bridges.inverse_map_function(bridge, value)
catch err
# MapNotInvertible is thrown if the bridge does not support inverting
# the function. The user doesn't need to know this, only that they
# cannot get the attribute.
if err isa MOI.Bridges.MapNotInvertible
msg = _not_invertible_error_message(attr, err.message)
throw(MOI.GetAttributeNotAllowed(attr, msg))
end
rethrow(err)
end
end

function MOI.set(
Expand All @@ -158,7 +185,7 @@ function MOI.set(
if value === nothing
MOI.set(model, attr, bridge.constraint, nothing)
else
mapped_value = MOI.Bridges.map_function(typeof(bridge), value)
mapped_value = MOI.Bridges.map_function(bridge, value)
MOI.set(model, attr, bridge.constraint, mapped_value)
end
return
Expand All @@ -173,7 +200,7 @@ function MOI.get(
if value === nothing
return nothing
end
return MOI.Bridges.adjoint_map_function(typeof(bridge), value)
return MOI.Bridges.adjoint_map_function(bridge, value)
end

function MOI.set(
Expand All @@ -185,7 +212,15 @@ function MOI.set(
if value === nothing
MOI.set(model, attr, bridge.constraint, nothing)
else
mapped_value = MOI.Bridges.inverse_adjoint_map_function(BT, value)
mapped_value = try
MOI.Bridges.inverse_adjoint_map_function(bridge, value)
catch err
if err isa MOI.Bridges.MapNotInvertible
msg = _not_invertible_error_message(attr, err.message)
throw(MOI.SetAttributeNotAllowed(attr, msg))
end
rethrow(err)
end
MOI.set(model, attr, bridge.constraint, mapped_value)
end
return
Expand Down
113 changes: 85 additions & 28 deletions src/Bridges/set_map.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,69 @@
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.

"""
struct MapNotInvertible <: Exception
message::String
end
An error thrown by [`inverse_map_function`](@ref) or
[`inverse_adjoint_map_function`](@ref) indicating that the linear map `A`
defined in [`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref)
is not invertible.
"""
struct MapNotInvertible <: Exception
message::String
end

"""
map_set(bridge::MOI.Bridges.AbstractBridge, set)
map_set(::Type{BT}, set) where {BT}
Return the image of `set` through the linear map `A` defined in
[`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref). This is
used for bridging the constraint and setting
the [`MOI.ConstraintSet`](@ref).
[`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref).
This function is used for bridging the constraint and setting the
[`MOI.ConstraintSet`](@ref).
"""
function map_set end
map_set(bridge::AbstractBridge, set) = map_set(typeof(bridge), set)

"""
inverse_map_set(bridge::MOI.Bridges.AbstractBridge, set)
inverse_map_set(::Type{BT}, set) where {BT}
Return the preimage of `set` through the linear map `A` defined in
[`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref). This is
used for getting the [`MOI.ConstraintSet`](@ref).
[`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref).
This function is used for getting the [`MOI.ConstraintSet`](@ref).
The method can alternatively be defined on the bridge type. This legacy
interface is kept for backward compatibility.
"""
function inverse_map_set end
function inverse_map_set(bridge::AbstractBridge, set)
return inverse_map_set(typeof(bridge), set)
end

"""
map_function(bridge::MOI.Bridges.AbstractBridge, func)
map_function(::Type{BT}, func) where {BT}
Return the image of `func` through the linear map `A` defined in
[`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref). This is
used for getting the [`MOI.ConstraintPrimal`](@ref) of variable
[`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref).
This function is used for getting the [`MOI.ConstraintPrimal`](@ref) of variable
bridges. For constraint bridges, this is used for bridging the constraint,
setting the [`MOI.ConstraintFunction`](@ref) and
[`MOI.ConstraintPrimalStart`](@ref) and
modifying the function with [`MOI.modify`](@ref).
setting the [`MOI.ConstraintFunction`](@ref) and [`MOI.ConstraintPrimalStart`](@ref)
and modifying the function with [`MOI.modify`](@ref).
The default implementation of [`Constraint.bridge_constraint`](@ref) uses
[`map_function`](@ref) with the bridge type so if this function is defined
on the bridge type, [`Constraint.bridge_constraint`](@ref) does not need
to be implemented.
"""
function map_function(bridge::AbstractBridge, func)
return map_function(typeof(bridge), func)
end

"""
map_function(::Type{BT}, func, i::IndexInVector) where {BT}
Return the scalar function at the `i`th index of the vector function that
Expand All @@ -42,42 +76,65 @@ would be returned by `map_function(BT, func)` except that it may compute the
the [`MOI.VariablePrimal`](@ref) and
[`MOI.VariablePrimalStart`](@ref) of variable bridges.
"""
function map_function end

function map_function(::Type{BT}, func, i::IndexInVector) where {BT}
return MOI.Utilities.eachscalar(map_function(BT, func))[i.value]
end

"""
inverse_map_function(bridge::MOI.Bridges.AbstractBridge, func)
inverse_map_function(::Type{BT}, func) where {BT}
Return the image of `func` through the inverse of the linear map `A` defined in
[`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref). This is
used by [`Variable.unbridged_map`](@ref) and for setting the
[`MOI.VariablePrimalStart`](@ref) of variable bridges
and for getting the [`MOI.ConstraintFunction`](@ref),
the [`MOI.ConstraintPrimal`](@ref) and the
[`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref).
This function is used by [`Variable.unbridged_map`](@ref) and for setting the
[`MOI.VariablePrimalStart`](@ref) of variable bridges and for getting the
[`MOI.ConstraintFunction`](@ref), the [`MOI.ConstraintPrimal`](@ref) and the
[`MOI.ConstraintPrimalStart`](@ref) of constraint bridges.
If the linear map `A` is not invertible, the error [`MapNotInvertible`](@ref) is
thrown.
The method can alternatively be defined on the bridge type. This legacy
interface is kept for backward compatibility.
"""
function inverse_map_function end
function inverse_map_function(bridge::AbstractBridge, func)
return inverse_map_function(typeof(bridge), func)
end

"""
adjoint_map_function(bridge::MOI.Bridges.AbstractBridge, func)
adjoint_map_function(::Type{BT}, func) where {BT}
Return the image of `func` through the adjoint of the linear map `A` defined in
[`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref). This is
used for getting the [`MOI.ConstraintDual`](@ref) and
[`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref).
This function is used for getting the [`MOI.ConstraintDual`](@ref) and
[`MOI.ConstraintDualStart`](@ref) of constraint bridges.
The method can alternatively be defined on the bridge type. This legacy
interface is kept for backward compatibility.
"""
function adjoint_map_function end
function adjoint_map_function(bridge::AbstractBridge, func)
return adjoint_map_function(typeof(bridge), func)
end

"""
inverse_adjoint_map_function(bridge::MOI.Bridges.AbstractBridge, func)
inverse_adjoint_map_function(::Type{BT}, func) where {BT}
Return the image of `func` through the inverse of the adjoint of the linear map
`A` defined in [`Variable.SetMapBridge`](@ref) and
[`Constraint.SetMapBridge`](@ref). This is used for getting the
[`MOI.ConstraintDual`](@ref) of variable bridges and setting the
[`MOI.ConstraintDualStart`](@ref) of constraint bridges.
`A` defined in [`Variable.SetMapBridge`](@ref) and [`Constraint.SetMapBridge`](@ref).
This function is used for getting the [`MOI.ConstraintDual`](@ref) of variable
bridges and setting the [`MOI.ConstraintDualStart`](@ref) of constraint bridges.
If the linear map `A` is not invertible, the error [`MapNotInvertible`](@ref) is
thrown.
The method can alternatively be defined on the bridge type. This legacy
interface is kept for backward compatibility.
"""
function inverse_adjoint_map_function end
function inverse_adjoint_map_function(bridge::AbstractBridge, func)
return inverse_adjoint_map_function(typeof(bridge), func)
end
Loading

0 comments on commit 85a1db5

Please sign in to comment.