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 set for low-rank constrained SDP #2198

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
17 changes: 9 additions & 8 deletions src/Bridges/Variable/set_map.jl
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@
bridge::SetMapBridge,
)
set = MOI.get(model, attr, bridge.constraint)
return MOI.Bridges.map_set(typeof(bridge), set)
return MOI.Bridges.map_set(bridge, set)

Check warning on line 132 in src/Bridges/Variable/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/Variable/set_map.jl#L132

Added line #L132 was not covered by tests
end

function MOI.set(
Expand All @@ -149,7 +149,7 @@
bridge::SetMapBridge,
)
value = MOI.get(model, attr, bridge.constraint)
return MOI.Bridges.map_function(typeof(bridge), value)
return MOI.Bridges.map_function(bridge, value)

Check warning on line 152 in src/Bridges/Variable/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/Variable/set_map.jl#L152

Added line #L152 was not covered by tests
end

function MOI.get(
Expand All @@ -171,7 +171,7 @@
if any(isnothing, value)
return nothing
end
return MOI.Bridges.map_function(typeof(bridge), value, i)
return MOI.Bridges.map_function(bridge, value, i)

Check warning on line 174 in src/Bridges/Variable/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/Variable/set_map.jl#L174

Added line #L174 was not covered by tests
end

function MOI.supports(
Expand All @@ -192,7 +192,7 @@
if value === nothing
MOI.set(model, attr, bridge.variables[i.value], nothing)
else
bridged_value = MOI.Bridges.inverse_map_function(typeof(bridge), value)
bridged_value = MOI.Bridges.inverse_map_function(bridge, value)

Check warning on line 195 in src/Bridges/Variable/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/Variable/set_map.jl#L195

Added line #L195 was not covered by tests
MOI.set(model, attr, bridge.variables[i.value], bridged_value)
end
return
Expand All @@ -203,7 +203,7 @@
i::MOI.Bridges.IndexInVector,
) where {T}
func = MOI.Bridges.map_function(
typeof(bridge),
bridge,
MOI.VectorOfVariables(bridge.variables),
i,
)
Expand All @@ -212,7 +212,7 @@

function unbridged_map(bridge::SetMapBridge{T}, vi::MOI.VariableIndex) where {T}
F = MOI.ScalarAffineFunction{T}
mapped = MOI.Bridges.inverse_map_function(typeof(bridge), vi)
mapped = MOI.Bridges.inverse_map_function(bridge, vi)

Check warning on line 215 in src/Bridges/Variable/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/Variable/set_map.jl#L215

Added line #L215 was not covered by tests
return Pair{MOI.VariableIndex,F}[bridge.variable=>mapped]
end

Expand All @@ -222,9 +222,10 @@
) where {T}
F = MOI.ScalarAffineFunction{T}
func = MOI.VectorOfVariables(vis)
funcs = MOI.Bridges.inverse_map_function(typeof(bridge), func)
funcs = MOI.Bridges.inverse_map_function(bridge, func)

Check warning on line 225 in src/Bridges/Variable/set_map.jl

View check run for this annotation

Codecov / codecov/patch

src/Bridges/Variable/set_map.jl#L225

Added line #L225 was not covered by tests
scalars = MOI.Utilities.eachscalar(funcs)
# FIXME not correct for SetDotProducts, it won't recover the dot product variables
return Pair{MOI.VariableIndex,F}[
bridge.variables[i] => scalars[i] for i in eachindex(vis)
bridge.variables[i] => scalars[i] for i in eachindex(bridge.variables)
]
end
4 changes: 4 additions & 0 deletions src/Bridges/set_map.jl
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@
return MOI.Utilities.eachscalar(map_function(BT, func))[i.value]
end

function map_function(bridge::AbstractBridge, func, i::IndexInVector)
return map_function(typeof(bridge), func, i)

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

View check run for this annotation

Codecov / codecov/patch

src/Bridges/set_map.jl#L83-L84

Added lines #L83 - L84 were not covered by tests
end

"""
inverse_map_function(bridge::MOI.Bridges.AbstractBridge, func)
inverse_map_function(::Type{BT}, func) where {BT}
Expand Down
10 changes: 8 additions & 2 deletions src/Utilities/functions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -638,18 +638,24 @@ end
A type that allows iterating over the scalar-functions that comprise an
`AbstractVectorFunction`.
"""
struct ScalarFunctionIterator{F<:MOI.AbstractVectorFunction,C}
struct ScalarFunctionIterator{F<:MOI.AbstractVectorFunction,C,S} <:
AbstractVector{S}
f::F
# Cache that can be used to store a precomputed datastructure that allows
# an efficient implementation of `getindex`.
cache::C
function ScalarFunctionIterator(f::MOI.AbstractVectorFunction, cache)
return new{typeof(f),typeof(cache),scalar_type(typeof(f))}(f, cache)
end
end

function ScalarFunctionIterator(func::MOI.AbstractVectorFunction)
return ScalarFunctionIterator(func, scalar_iterator_cache(func))
end

scalar_iterator_cache(func::MOI.AbstractVectorFunction) = nothing
Base.size(s::ScalarFunctionIterator) = (MOI.output_dimension(s.f),)

scalar_iterator_cache(::MOI.AbstractVectorFunction) = nothing

function output_index_iterator(terms::AbstractVector, output_dimension)
start = zeros(Int, output_dimension)
Expand Down
100 changes: 80 additions & 20 deletions test/Bridges/set_map.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,44 @@ end

MOI.dimension(::SwapSet) = 2

struct SwapBridge{T} <: MOI.Bridges.Constraint.SetMapBridge{
struct VariableSwapBridge{T} <:
MOI.Bridges.Variable.SetMapBridge{T,MOI.Nonnegatives,SwapSet}
variables::MOI.Vector{MOI.VariableIndex}
constraint::MOI.ConstraintIndex{MOI.VectorOfVariables,MOI.Nonnegatives}
set::SwapSet
end

function MOI.Bridges.Variable.bridge_constrained_variable(
::Type{VariableSwapBridge{T}},
model::MOI.ModelLike,
set::SwapSet,
) where {T}
variables, constraint =
MOI.add_constrained_variables(model, MOI.Nonnegatives(2))
return VariableSwapBridge{T}(variables, constraint, set)
end

MOI.Bridges.map_set(bridge::VariableSwapBridge, ::MOI.Nonnegatives) = bridge.set

function MOI.Bridges.inverse_map_set(bridge::VariableSwapBridge, set::SwapSet)
if set.swap != bridge.set.swap
error("Cannot change swap set")
end
return MOI.Nonnegatives(2)
end

function MOI.Bridges.map_function(
bridge::VariableSwapBridge,
func,
i::MOI.Bridges.IndexInVector,
)
return MOI.Bridges.map_function(bridge, func)[i.value]
end

# Workaround until https://github.com/jump-dev/MathOptInterface.jl/issues/2117 is fixed
MOI.Bridges.inverse_map_function(::VariableSwapBridge, a::Float64) = a

struct ConstraintSwapBridge{T} <: MOI.Bridges.Constraint.SetMapBridge{
T,
MOI.Nonnegatives,
SwapSet,
Expand All @@ -34,7 +71,7 @@ struct SwapBridge{T} <: MOI.Bridges.Constraint.SetMapBridge{
end

function MOI.Bridges.Constraint.bridge_constraint(
::Type{SwapBridge{T}},
::Type{ConstraintSwapBridge{T}},
model::MOI.ModelLike,
func::MOI.VectorOfVariables,
set::SwapSet,
Expand All @@ -44,17 +81,24 @@ function MOI.Bridges.Constraint.bridge_constraint(
MOI.VectorOfVariables(swap(func.variables, set.swap)),
MOI.Nonnegatives(2),
)
return SwapBridge{T}(ci, set)
return ConstraintSwapBridge{T}(ci, set)
end

function MOI.Bridges.map_set(bridge::SwapBridge, set::SwapSet)
function MOI.Bridges.map_set(bridge::ConstraintSwapBridge, set::SwapSet)
if set.swap != bridge.set.swap
error("Cannot change swap set")
end
return MOI.Nonnegatives(2)
end

MOI.Bridges.inverse_map_set(bridge::SwapBridge, ::MOI.Nonnegatives) = bridge.set
function MOI.Bridges.inverse_map_set(
bridge::ConstraintSwapBridge,
::MOI.Nonnegatives,
)
return bridge.set
end

const SwapBridge{T} = Union{VariableSwapBridge{T},ConstraintSwapBridge{T}}

function MOI.Bridges.map_function(bridge::SwapBridge, func)
return swap(func, bridge.set.swap)
Expand Down Expand Up @@ -90,19 +134,10 @@ function swap(f::MOI.VectorOfVariables, do_swap::Bool)
return MOI.VectorOfVariables(swap(f.variables, do_swap))
end

function runtests()
for name in names(@__MODULE__; all = true)
if startswith("$(name)", "test_")
@testset "$(name)" begin
getfield(@__MODULE__, name)()
end
end
end
return
end

function test_other_error()
model = MOI.Bridges.Constraint.SingleBridgeOptimizer{SwapBridge{Float64}}(
model = MOI.Bridges.Constraint.SingleBridgeOptimizer{
ConstraintSwapBridge{Float64},
}(
MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
)
x = MOI.add_variables(model, 2)
Expand All @@ -123,8 +158,11 @@ function test_other_error()
)
return
end
function test_not_invertible()
model = MOI.Bridges.Constraint.SingleBridgeOptimizer{SwapBridge{Float64}}(

function test_constraint_not_invertible()
model = MOI.Bridges.Constraint.SingleBridgeOptimizer{
ConstraintSwapBridge{Float64},
}(
MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
)
x = MOI.add_variables(model, 2)
Expand Down Expand Up @@ -162,7 +200,7 @@ end
function test_runtests()
for do_swap in [false, true]
MOI.Bridges.runtests(
SwapBridge,
ConstraintSwapBridge,
model -> begin
x = MOI.add_variables(model, 2)
func = MOI.VectorOfVariables(x)
Expand All @@ -176,6 +214,28 @@ function test_runtests()
MOI.add_constraint(model, func, set)
end,
)
MOI.Bridges.runtests(
VariableSwapBridge,
model -> begin
set = SwapSet(do_swap, NONE)
x = MOI.add_constrained_variables(model, set)
end,
model -> begin
set = MOI.Nonnegatives(2)
x = MOI.add_constrained_variables(model, set)
end,
)
end
return
end

function runtests()
for name in names(@__MODULE__; all = true)
if startswith("$(name)", "test_")
@testset "$(name)" begin
getfield(@__MODULE__, name)()
end
end
end
return
end
Expand Down
4 changes: 4 additions & 0 deletions test/Utilities/functions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,8 @@ end
function test_iteration_and_indexing_on_VectorOfVariables()
f = MOI.VectorOfVariables([z, w, x, y])
it = MOI.Utilities.eachscalar(f)
@test it isa AbstractVector{MOI.VariableIndex}
@test size(it) == (4,)
@test length(it) == 4
@test eltype(it) == MOI.VariableIndex
@test collect(it) == [z, w, x, y]
Expand All @@ -454,6 +456,8 @@ function test_indexing_on_VectorAffineFunction()
[2, 7, 5],
)
it = MOI.Utilities.eachscalar(f)
@test it isa AbstractVector{MOI.ScalarAffineFunction{Int}}
@test size(it) == (3,)
@test length(it) == 3
@test eltype(it) == MOI.ScalarAffineFunction{Int}
g = it[2]
Expand Down
Loading