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

Allow SetMapBridge to use bridge value #2509

Merged
merged 15 commits into from
Jun 22, 2024
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 @@

# 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"

Check warning on line 104 in src/Bridges/Constraint/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/Constraint/set_map.jl#L103-L104

Added lines #L103 - L104 were not covered by tests
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)

Check warning on line 114 in src/Bridges/Constraint/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/Constraint/set_map.jl#L113-L114

Added lines #L113 - L114 were not covered by tests
catch err
if err isa MOI.Bridges.MapNotInvertible
msg = _not_invertible_error_message(attr, err.message)
throw(MOI.GetAttributeNotAllowed(attr, msg))

Check warning on line 118 in src/Bridges/Constraint/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/Constraint/set_map.jl#L116-L118

Added lines #L116 - L118 were not covered by tests
end
rethrow(err)

Check warning on line 120 in src/Bridges/Constraint/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/Constraint/set_map.jl#L120

Added line #L120 was not covered by tests
end
return MOI.Utilities.convert_approx(G, func)
end

Expand All @@ -123,7 +139,7 @@
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)

Check warning on line 142 in src/Bridges/Constraint/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/Constraint/set_map.jl#L142

Added line #L142 was not covered by tests
end

function MOI.set(
Expand All @@ -132,7 +148,7 @@
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)

Check warning on line 151 in src/Bridges/Constraint/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/Constraint/set_map.jl#L151

Added line #L151 was not covered by tests
MOI.set(model, attr, bridge.constraint, new_set)
return
end
Expand All @@ -146,7 +162,18 @@
if value === nothing
return nothing
end
return MOI.Bridges.inverse_map_function(typeof(bridge), value)
try
return MOI.Bridges.inverse_map_function(bridge, value)

Check warning on line 166 in src/Bridges/Constraint/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/Constraint/set_map.jl#L165-L166

Added lines #L165 - L166 were not covered by tests
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))

Check warning on line 173 in src/Bridges/Constraint/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/Constraint/set_map.jl#L171-L173

Added lines #L171 - L173 were not covered by tests
end
rethrow(err)

Check warning on line 175 in src/Bridges/Constraint/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/Constraint/set_map.jl#L175

Added line #L175 was not covered by tests
end
end

function MOI.set(
Expand All @@ -158,7 +185,7 @@
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)

Check warning on line 188 in src/Bridges/Constraint/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/Constraint/set_map.jl#L188

Added line #L188 was not covered by tests
MOI.set(model, attr, bridge.constraint, mapped_value)
end
return
Expand All @@ -173,7 +200,7 @@
if value === nothing
return nothing
end
return MOI.Bridges.adjoint_map_function(typeof(bridge), value)
return MOI.Bridges.adjoint_map_function(bridge, value)

Check warning on line 203 in src/Bridges/Constraint/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/Constraint/set_map.jl#L203

Added line #L203 was not covered by tests
end

function MOI.set(
Expand All @@ -185,7 +212,15 @@
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)

Check warning on line 216 in src/Bridges/Constraint/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/Constraint/set_map.jl#L215-L216

Added lines #L215 - L216 were not covered by tests
catch err
if err isa MOI.Bridges.MapNotInvertible
msg = _not_invertible_error_message(attr, err.message)
throw(MOI.SetAttributeNotAllowed(attr, msg))

Check warning on line 220 in src/Bridges/Constraint/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/Constraint/set_map.jl#L218-L220

Added lines #L218 - L220 were not covered by tests
end
rethrow(err)

Check warning on line 222 in src/Bridges/Constraint/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/Constraint/set_map.jl#L222

Added line #L222 was not covered by tests
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)

Check warning on line 31 in src/Bridges/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/set_map.jl#L31

Added line #L31 was not covered by tests

"""
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)

Check warning on line 46 in src/Bridges/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/set_map.jl#L45-L46

Added lines #L45 - L46 were not covered by tests
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)

Check warning on line 67 in src/Bridges/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/set_map.jl#L66-L67

Added lines #L66 - L67 were not covered by tests
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 @@
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}
odow marked this conversation as resolved.
Show resolved Hide resolved
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)

Check warning on line 102 in src/Bridges/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/set_map.jl#L101-L102

Added lines #L101 - L102 were not covered by tests
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)

Check warning on line 119 in src/Bridges/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/set_map.jl#L118-L119

Added lines #L118 - L119 were not covered by tests
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)
odow marked this conversation as resolved.
Show resolved Hide resolved
return inverse_adjoint_map_function(typeof(bridge), func)

Check warning on line 139 in src/Bridges/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/set_map.jl#L138-L139

Added lines #L138 - L139 were not covered by tests
end
Loading
Loading