Skip to content

Commit 82cd0b1

Browse files
authored
Merge 6e571af into 6bfca94
2 parents 6bfca94 + 6e571af commit 82cd0b1

5 files changed

Lines changed: 175 additions & 13 deletions

File tree

src/Bridges/Variable/map.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,8 @@ Base.keys(::EmptyMap) = MOI.Utilities.EmptyVector{MOI.VariableIndex}()
691691

692692
Base.values(::EmptyMap) = MOI.Utilities.EmptyVector{AbstractBridge}()
693693

694+
Base.iterate(::EmptyMap) = nothing
695+
694696
has_bridges(::EmptyMap) = false
695697

696698
number_of_variables(::EmptyMap) = 0

src/Bridges/bridge_optimizer.jl

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -717,12 +717,50 @@ function _get_all_including_bridged(
717717
b::AbstractBridgeOptimizer,
718718
attr::MOI.ListOfVariableIndices,
719719
)
720-
list = MOI.get(b.model, attr)
721-
if !isempty(Variable.bridges(b))
722-
# The conversion from `keys` into a `Vector` happens inside `append!`.
723-
append!(list, keys(Variable.bridges(b)))
720+
# `inner_to_outer` is going to map variable indices in `b.model` to their
721+
# bridged variable indices. If the bridge adds multiple variables, we need
722+
# only to map the first variable, and we can skip the rest. To mark this
723+
# distinction, the tail variables are set to `nothing`.
724+
map = Variable.bridges(b)
725+
inner_to_outer = Dict{MOI.VariableIndex,Union{Nothing,MOI.VariableIndex}}()
726+
for (user_variable, bridge) in map
727+
variables = MOI.get(bridge, MOI.ListOfVariableIndices())
728+
if !isempty(variables)
729+
# Some bridges, like Zeros, don't add any variables.
730+
for bridged_variable in variables
731+
inner_to_outer[bridged_variable] = nothing
732+
end
733+
inner_to_outer[first(variables)] = user_variable
734+
end
724735
end
725-
return list
736+
# We're about to loop over the variables in `.model`, ordered by when they
737+
# were added to the model. We need to return a list of the original
738+
# user-space variables in the order that they were added. To do so, we need
739+
# to undo any Variable.bridges transformations.
740+
ret = MOI.VariableIndex[]
741+
for inner_variable in MOI.get(b.model, attr)
742+
outer_variable = get(inner_to_outer, inner_variable, missing)
743+
if ismissing(outer_variable)
744+
# inner_variable does not exist in inner_to_outer, which means that
745+
# it is not bridged. Pass through unchanged.
746+
push!(ret, inner_variable)
747+
elseif isnothing(outer_variable)
748+
# inner_variable exists in inner_to_outer, but it is set to `nothing`
749+
# which means that it is not the first variable in the bridge. Skip
750+
# it because it should be hidden from the user.
751+
else
752+
# inner_variable exists in inner_to_outer. It must be the first
753+
# variable in the bridge. Report it back to the user.
754+
push!(ret, outer_variable)
755+
# `outer_variable` might represent the start of a VectorOfVariables
756+
# if multiple user-variables were bridged. Add them all.
757+
n = Variable.length_of_vector_of_variables(map, outer_variable)
758+
for i in 1:n-1
759+
push!(ret, MOI.VariableIndex(outer_variable.value - i))
760+
end
761+
end
762+
end
763+
return ret
726764
end
727765

728766
function _get_all_including_bridged(

src/Test/test_basic_constraint.jl

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,6 @@ function _basic_constraint_test_helper(
219219
set = _set(T, UntypedS)
220220
N = MOI.dimension(set)
221221
x = add_variables_fn(model, N)
222-
223222
constraint_function = _function(T, UntypedF, x)
224223
@assert MOI.output_dimension(constraint_function) == N
225224
F, S = typeof(constraint_function), typeof(set)
@@ -235,9 +234,21 @@ function _basic_constraint_test_helper(
235234
@test MOI.get(model, MOI.NumberOfConstraints{F,S}()) == 1
236235
_test_attribute_value_type(model, MOI.NumberOfConstraints{F,S}())
237236
###
237+
### Test MOI.ListOfConstraintIndices
238+
###
239+
c_indices = MOI.get(model, MOI.ListOfConstraintIndices{F,S}())
240+
@test c_indices == [c]
241+
###
242+
### Test MOI.ListOfConstraintTypesPresent
243+
###
244+
@test (F, S) in MOI.get(model, MOI.ListOfConstraintTypesPresent())
245+
###
238246
### Test MOI.is_valid
239247
###
240248
@test MOI.is_valid(model, c)
249+
# TODO(odow): we could improve this test by checking `c.value+1` and
250+
# `c.value-1` but there is a bug in `LazyBridgeOptimizer`.
251+
@test !MOI.is_valid(model, typeof(c)(c.value + 12345))
241252
###
242253
### Test MOI.ConstraintName
243254
###
@@ -287,11 +298,6 @@ function _basic_constraint_test_helper(
287298
_test_attribute_value_type(model, MOI.ConstraintSet(), c)
288299
end
289300
###
290-
### Test MOI.ListOfConstraintIndices
291-
###
292-
c_indices = MOI.get(model, MOI.ListOfConstraintIndices{F,S}())
293-
@test length(c_indices) == 1
294-
###
295301
### Test MOI.add_constraints
296302
###
297303
if F != MOI.VariableIndex && F != MOI.VectorOfVariables

src/Test/test_conic.jl

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7219,3 +7219,117 @@ function setup_test(
72197219
end
72207220

72217221
version_added(::typeof(test_conic_NormCone)) = v"1.14.0"
7222+
7223+
function test_add_constrained_PositiveSemidefiniteConeTriangle(
7224+
model::MOI.ModelLike,
7225+
config::Config{T},
7226+
) where {T<:Real}
7227+
@requires MOI.supports_add_constrained_variables(
7228+
model,
7229+
MOI.PositiveSemidefiniteConeTriangle,
7230+
)
7231+
@requires _supports(config, MOI.delete)
7232+
# Add a scalar
7233+
x = MOI.add_variable(model)
7234+
@test MOI.get(model, MOI.ListOfVariableIndices()) == [x]
7235+
@test MOI.is_valid(model, x)
7236+
@test MOI.get(model, MOI.NumberOfVariables()) == 1
7237+
# Add a PSD matrix
7238+
set = MOI.PositiveSemidefiniteConeTriangle(2)
7239+
X, c = MOI.add_constrained_variables(model, set)
7240+
F, S = MOI.VectorOfVariables, MOI.PositiveSemidefiniteConeTriangle
7241+
@test (F, S) in MOI.get(model, MOI.ListOfConstraintTypesPresent())
7242+
@test MOI.get(model, MOI.ListOfConstraintIndices{F,S}()) == [c]
7243+
@test MOI.get(model, MOI.NumberOfConstraints{F,S}()) == 1
7244+
@test MOI.is_valid(model, c)
7245+
@test !MOI.is_valid(model, typeof(c)(c.value - 1))
7246+
@test !MOI.is_valid(model, typeof(c)(c.value + 1))
7247+
@test MOI.get(model, MOI.ListOfVariableIndices()) == [x; X]
7248+
@test all(MOI.is_valid.(model, [x; X]))
7249+
@test MOI.get(model, MOI.NumberOfVariables()) == 4
7250+
# Add and delete a scalar
7251+
y = MOI.add_variable(model)
7252+
@test MOI.get(model, MOI.ListOfVariableIndices()) == [x; X; y]
7253+
@test all(MOI.is_valid.(model, [x; X; y]))
7254+
@test MOI.get(model, MOI.NumberOfVariables()) == 5
7255+
MOI.delete(model, y)
7256+
@test !MOI.is_valid(model, y)
7257+
@test MOI.get(model, MOI.ListOfVariableIndices()) == [x; X]
7258+
@test all(MOI.is_valid.(model, [x; X]))
7259+
@test MOI.get(model, MOI.NumberOfVariables()) == 4
7260+
# Add and delete another scalar
7261+
z = MOI.add_variable(model)
7262+
@test MOI.get(model, MOI.ListOfVariableIndices()) == [x; X; z]
7263+
@test all(MOI.is_valid.(model, [x; X]))
7264+
@test MOI.is_valid(model, z)
7265+
@test MOI.get(model, MOI.NumberOfVariables()) == 5
7266+
MOI.delete(model, z)
7267+
@test !MOI.is_valid(model, y)
7268+
@test !MOI.is_valid(model, z)
7269+
@test MOI.get(model, MOI.ListOfVariableIndices()) == [x; X]
7270+
@test all(MOI.is_valid.(model, [x; X]))
7271+
@test MOI.get(model, MOI.NumberOfVariables()) == 4
7272+
MOI.delete(model, x)
7273+
@test !MOI.is_valid(model, x)
7274+
@test !MOI.is_valid(model, y)
7275+
@test !MOI.is_valid(model, z)
7276+
@test MOI.get(model, MOI.ListOfVariableIndices()) == X
7277+
@test all(MOI.is_valid.(model, X))
7278+
@test MOI.get(model, MOI.NumberOfVariables()) == 3
7279+
return
7280+
end
7281+
7282+
function version_added(
7283+
::typeof(test_add_constrained_PositiveSemidefiniteConeTriangle),
7284+
)
7285+
return v"1.38.1"
7286+
end
7287+
7288+
function test_add_constrained_PositiveSemidefiniteConeTriangle_VariableName(
7289+
model::MOI.ModelLike,
7290+
config::Config{T},
7291+
) where {T<:Real}
7292+
@requires MOI.supports_add_constrained_variables(
7293+
model,
7294+
MOI.PositiveSemidefiniteConeTriangle,
7295+
)
7296+
@requires MOI.supports(model, MOI.VariableName(), MOI.VariableIndex)
7297+
set = MOI.PositiveSemidefiniteConeTriangle(2)
7298+
X, _ = MOI.add_constrained_variables(model, set)
7299+
MOI.set(model, MOI.VariableName(), X[1], "x")
7300+
@test MOI.get(model, MOI.VariableName(), X[1]) == "x"
7301+
return
7302+
end
7303+
7304+
function version_added(
7305+
::typeof(
7306+
test_add_constrained_PositiveSemidefiniteConeTriangle_VariableName,
7307+
),
7308+
)
7309+
return v"1.38.1"
7310+
end
7311+
7312+
function test_add_constrained_PositiveSemidefiniteConeTriangle_VariablePrimalStart(
7313+
model::MOI.ModelLike,
7314+
config::Config{T},
7315+
) where {T<:Real}
7316+
@requires MOI.supports_add_constrained_variables(
7317+
model,
7318+
MOI.PositiveSemidefiniteConeTriangle,
7319+
)
7320+
@requires MOI.supports(model, MOI.VariablePrimalStart(), MOI.VariableIndex)
7321+
set = MOI.PositiveSemidefiniteConeTriangle(2)
7322+
X, _ = MOI.add_constrained_variables(model, set)
7323+
@test all(isnothing, MOI.get.(model, MOI.VariablePrimalStart(), X))
7324+
MOI.set.(model, MOI.VariablePrimalStart(), X, [1.0, 0.0, 1.0])
7325+
@test MOI.get.(model, MOI.VariablePrimalStart(), X) == [1.0, 0.0, 1.0]
7326+
return
7327+
end
7328+
7329+
function version_added(
7330+
::typeof(
7331+
test_add_constrained_PositiveSemidefiniteConeTriangle_VariablePrimalStart,
7332+
),
7333+
)
7334+
return v"1.38.1"
7335+
end

test/Bridges/Variable/NonposToNonnegBridge.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,10 @@ function test_NonposToNonneg()
4040
)
4141
@test MOI.get(mock, MOI.NumberOfVariables()) == 4
4242
@test MOI.get(bridged_mock, MOI.NumberOfVariables()) == 4
43+
# Variables are ordered
44+
# x in R^1, y in R_-^1, z in R^1, s in R^1
4345
vis = MOI.get(bridged_mock, MOI.ListOfVariableIndices())
44-
y = vis[4]
46+
y = vis[2]
4547
@test y.value == -1
4648

4749
@test MOI.supports(
@@ -90,7 +92,7 @@ function test_NonposToNonneg()
9092
MOI.set(
9193
bridged_mock,
9294
MOI.VariableName(),
93-
MOI.get(bridged_mock, MOI.ListOfVariableIndices())[4],
95+
MOI.get(bridged_mock, MOI.ListOfVariableIndices())[2],
9496
"v",
9597
)
9698
con_v = MOI.get(

0 commit comments

Comments
 (0)