Skip to content

Commit 6a99196

Browse files
committed
Add support for deleting variable in vector
1 parent b3ac168 commit 6a99196

File tree

10 files changed

+232
-80
lines changed

10 files changed

+232
-80
lines changed

src/Bridges/Variable/flip_sign.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ function MOI.delete(model::MOI.ModelLike, bridge::FlipSignBridge)
4242
MOI.delete(model, bridge.flipped_variables)
4343
end
4444

45+
function MOI.delete(model::MOI.ModelLike, bridge::FlipSignBridge, i::IndexInVector)
46+
MOI.delete(model, bridge.flipped_variables[i.value])
47+
deleteat!(bridge.flipped_variables, i.value)
48+
end
4549

4650
# Attributes, Bridge acting as a constraint
4751

src/Bridges/Variable/free.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,14 @@ function MOI.delete(model::MOI.ModelLike, bridge::FreeBridge)
6767
MOI.delete(model, bridge.nonpos_variables)
6868
end
6969

70+
function MOI.delete(model::MOI.ModelLike, bridge::FreeBridge, i::IndexInVector)
71+
MOI.delete(model, bridge.nonneg_variables[i.value])
72+
deleteat!(bridge.nonneg_variables, i.value)
73+
MOI.delete(model, bridge.nonpos_variables[i.value])
74+
deleteat!(bridge.nonpos_variables, i.value)
75+
end
76+
77+
7078
# Attributes, Bridge acting as a constraint
7179

7280
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimal,

src/Bridges/Variable/map.jl

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,34 @@ Mapping between bridged variables and the bridge that bridged the variable.
66
mutable struct Map <: AbstractDict{MOI.VariableIndex, AbstractBridge}
77
# Bridged constrained variables
88
# `i` -> `0`: `VariableIndex(-i)` was added with `add_constrained_variable`.
9-
# `i` -> `-j`: `VariableIndex(-i)` is was the first variable of
9+
# `i` -> `-j`: `VariableIndex(-i)` was the first variable of
1010
# `add_constrained_variables` with a set of dimension `j`.
11-
# `i` -> `j`: `VariableIndex(-i)` is was the `j`th variable of
11+
# `i` -> `j`: `VariableIndex(-i)` was the `j`th variable of
1212
# ` add_constrained_variables`.
13-
index_in_vector_of_variables::Vector{Int}
13+
info::Vector{Int}
14+
# `i` -> `-1`: `VariableIndex(-i)` was deleted.
15+
# `i` -> `0`: `VariableIndex(-i)` was added with `add_constrained_variable`.
16+
# `i` -> `j`: `VariableIndex(-i)` is the `j`th variable of a constrained
17+
# vector of variables, taking deletion into account.
18+
index_in_vector::Vector{Int}
1419
# `i` -> `bridge`: `VariableIndex(-i)` was bridged by `bridge`.
1520
bridges::Vector{Union{Nothing, AbstractBridge}}
1621
sets::Vector{Union{Nothing, DataType}}
1722
# If `nothing`, it cannot be computed because some bridges does not support it
1823
unbridged_function::Union{Nothing, Dict{MOI.VariableIndex, MOI.AbstractScalarFunction}}
1924
end
20-
Map() = Map(Int[], Union{Nothing, AbstractBridge}[], Union{Nothing, DataType}[], Dict{MOI.VariableIndex, MOI.AbstractScalarFunction}())
25+
function Map()
26+
return Map(Int[], Int[], Union{Nothing, AbstractBridge}[],
27+
Union{Nothing, DataType}[],
28+
Dict{MOI.VariableIndex, MOI.AbstractScalarFunction}())
29+
end
2130

2231
# Implementation of `AbstractDict` interface.
2332

2433
Base.isempty(map::Map) = all(bridge -> bridge === nothing, map.bridges)
2534
function Base.empty!(map::Map)
26-
empty!(map.index_in_vector_of_variables)
35+
empty!(map.info)
36+
empty!(map.index_in_vector)
2737
empty!(map.bridges)
2838
empty!(map.sets)
2939
if map.unbridged_function === nothing
@@ -34,7 +44,7 @@ function Base.empty!(map::Map)
3444
return map
3545
end
3646
function bridge_index(map::Map, vi::MOI.VariableIndex)
37-
index = map.index_in_vector_of_variables[-vi.value]
47+
index = map.info[-vi.value]
3848
if index 0
3949
return -vi.value
4050
else
@@ -43,23 +53,45 @@ function bridge_index(map::Map, vi::MOI.VariableIndex)
4353
end
4454
function Base.haskey(map::Map, vi::MOI.VariableIndex)
4555
return -length(map.bridges) vi.value -1 &&
46-
map.bridges[bridge_index(map, vi)] !== nothing
56+
map.bridges[bridge_index(map, vi)] !== nothing &&
57+
map.index_in_vector[-vi.value] != -1
4758
end
4859
function Base.getindex(map::Map, vi::MOI.VariableIndex)
4960
return map.bridges[bridge_index(map, vi)]
5061
end
5162
function Base.delete!(map::Map, vi::MOI.VariableIndex)
52-
map.bridges[bridge_index(map, vi)] = nothing
53-
map.sets[bridge_index(map, vi)] = nothing
63+
if iszero(map.info[-vi.value])
64+
# Delete scalar variable
65+
map.bridges[bridge_index(map, vi)] = nothing
66+
map.sets[bridge_index(map, vi)] = nothing
67+
elseif has_keys(map, [vi])
68+
# Delete whole vector
69+
delete!(map, [vi])
70+
else
71+
# Delete variable in vector and resize vector
72+
map.info[bridge_index(map, vi)] += 1
73+
for i in (-vi.value):length(map.index_in_vector)
74+
if map.index_in_vector[i] == -1
75+
continue
76+
elseif bridge_index(map, vi) != bridge_index(map, MOI.VariableIndex(-i))
77+
break
78+
end
79+
map.index_in_vector[i] -= 1
80+
end
81+
end
82+
map.index_in_vector[-vi.value] = -1
5483
return map
5584
end
5685
function Base.delete!(map::Map, vis::Vector{MOI.VariableIndex})
5786
if has_keys(map, vis)
87+
for vi in vis
88+
map.index_in_vector[-vi.value] = -1
89+
end
5890
map.bridges[bridge_index(map, first(vis))] = nothing
5991
map.sets[bridge_index(map, first(vis))] = nothing
6092
return
6193
else
62-
throw(ArgumentError("$vis is not a valid key vector as returned by `add_keys_for_bridge`."))
94+
throw(ArgumentError("`$vis` is not a valid key vector as returned by `add_keys_for_bridge`."))
6395
end
6496
end
6597
function Base.keys(map::Map)
@@ -74,7 +106,7 @@ function number_of_variables(map::Map)
74106
num = 0
75107
for i in eachindex(map.bridges)
76108
if map.bridges[i] !== nothing
77-
if iszero(map.index_in_vector_of_variables[i])
109+
if iszero(map.info[i])
78110
num += 1
79111
else
80112
num += length_of_vector_of_variables(map, MOI.VariableIndex(-i))
@@ -157,9 +189,12 @@ Return a `Bool` indicating whether `vis` was returned by
157189
[`add_keys_for_bridge`](@ref) and has not been deleted yet.
158190
"""
159191
function has_keys(map::Map, vis::Vector{MOI.VariableIndex})
160-
return length_of_vector_of_variables(map, first(vis)) == length(vis) &&
161-
all(vi -> bridge_index(map, vi) == -first(vis).value, vis) &&
162-
haskey(map, vis[1])
192+
return isempty(vis) || (
193+
length_of_vector_of_variables(map, first(vis)) == length(vis) &&
194+
all(vi -> bridge_index(map, vi) == bridge_index(map, first(vis)), vis) &&
195+
all(vi -> haskey(map, vi), vis) &&
196+
all(i -> vis[i].value < vis[i - 1].value, 2:length(vis))
197+
)
163198
end
164199

165200
"""
@@ -169,7 +204,7 @@ If `vi` was bridged in a scalar set, it returns 0. Otherwise, it
169204
returns the dimension of the set.
170205
"""
171206
function length_of_vector_of_variables(map::Map, vi::MOI.VariableIndex)
172-
return -map.index_in_vector_of_variables[bridge_index(map, vi)]
207+
return -map.info[bridge_index(map, vi)]
173208
end
174209

175210
"""
@@ -178,11 +213,7 @@ end
178213
Return the index of `vi` in the vector of variables in which it was bridged.
179214
"""
180215
function index_in_vector_of_variables(map::Map, vi::MOI.VariableIndex)
181-
index = map.index_in_vector_of_variables[-vi.value]
182-
if index < 0
183-
index = 1
184-
end
185-
return IndexInVector(index)
216+
return IndexInVector(map.index_in_vector[-vi.value])
186217
end
187218

188219
"""
@@ -194,7 +225,7 @@ returns `false` even if all bridges were deleted while `isempty` would return
194225
by [`MathOptInterface.Bridges.AbstractBridgeOptimizer`](@ref) to shortcut
195226
operations in case variable bridges are not used.
196227
"""
197-
has_bridges(map::Map) = !isempty(map.index_in_vector_of_variables)
228+
has_bridges(map::Map) = !isempty(map.info)
198229

199230
"""
200231
add_key_for_bridge(map::Map, bridge::AbstractBridge,
@@ -209,7 +240,8 @@ function add_key_for_bridge(map::Map, bridge::AbstractBridge,
209240
set::MOI.AbstractScalarSet)
210241
index = -(length(map.bridges) + 1)
211242
variable = MOI.VariableIndex(index)
212-
push!(map.index_in_vector_of_variables, 0)
243+
push!(map.info, 0)
244+
push!(map.index_in_vector, 0)
213245
push!(map.bridges, bridge)
214246
push!(map.sets, typeof(set))
215247
if map.unbridged_function !== nothing
@@ -239,11 +271,13 @@ function add_keys_for_bridge(map::Map, bridge::AbstractBridge,
239271
else
240272
variables = MOI.VariableIndex[MOI.VariableIndex(-(length(map.bridges) + i))
241273
for i in 1:MOI.dimension(set)]
242-
push!(map.index_in_vector_of_variables, -MOI.dimension(set))
274+
push!(map.info, -MOI.dimension(set))
275+
push!(map.index_in_vector, 1)
243276
push!(map.bridges, bridge)
244277
push!(map.sets, typeof(set))
245278
for i in 2:MOI.dimension(set)
246-
push!(map.index_in_vector_of_variables, i)
279+
push!(map.info, i)
280+
push!(map.index_in_vector, i)
247281
push!(map.bridges, nothing)
248282
push!(map.sets, nothing)
249283
end
@@ -282,7 +316,9 @@ function function_for(map::Map, ci::MOI.ConstraintIndex{MOI.VectorOfVariables})
282316
variables = MOI.VariableIndex[]
283317
for i in ci.value:-1:-length(map.bridges)
284318
vi = MOI.VariableIndex(i)
285-
if bridge_index(map, vi) == -ci.value
319+
if map.index_in_vector[-vi.value] == -1
320+
continue
321+
elseif bridge_index(map, vi) == -ci.value
286322
push!(variables, vi)
287323
else
288324
break

src/Bridges/bridge_optimizer.jl

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,14 @@ function MOI.delete(b::AbstractBridgeOptimizer, vi::MOI.VariableIndex)
186186
if is_bridged(b, vi)
187187
MOI.throw_if_not_valid(b, vi)
188188
if Variable.length_of_vector_of_variables(Variable.bridges(b), vi) > 1
189-
MOIU.throw_delete_variable_in_vov(vi)
189+
if Variable.constrained_set(Variable.bridges(b), vi) <: MOIU.DimensionUpdatableSets
190+
MOI.delete(b, bridge(b, vi), _index(b, vi)...)
191+
else
192+
MOIU.throw_delete_variable_in_vov(vi)
193+
end
194+
else
195+
MOI.delete(b, bridge(b, vi))
190196
end
191-
MOI.delete(b, bridge(b, vi))
192197
delete!(Variable.bridges(b), vi)
193198
else
194199
MOI.delete(b.model, vi)

test/Bridges/Variable/flip_sign.jl

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,47 @@ config = MOIT.TestConfig()
1414
@testset "NonposToNonneg" begin
1515
bridged_mock = MOIB.Variable.NonposToNonneg{Float64}(mock)
1616

17-
mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [-4, 3, 16, 0],
18-
(MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[7, 2, -4]])
19-
MOIT.lin2vtest(bridged_mock, config)
20-
21-
@test MOI.get(mock, MOI.NumberOfVariables()) == 4
22-
@test MOI.get(bridged_mock, MOI.NumberOfVariables()) == 4
23-
vis = MOI.get(bridged_mock, MOI.ListOfVariableIndices())
24-
y = vis[4]
25-
@test y.value == -1
26-
27-
@test MOI.supports(bridged_mock, MOI.VariablePrimalStart(), MOI.VariableIndex)
28-
MOI.set(bridged_mock, MOI.VariablePrimalStart(), y, 1.0)
29-
x, y_flipped, z, s = MOI.get(mock, MOI.ListOfVariableIndices())
30-
@test MOI.get(mock, MOI.VariablePrimalStart(), y_flipped) == -1
31-
32-
MOIU.set_mock_optimize!(mock,
33-
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
34-
mock, MOI.INFEASIBLE, MOI.INFEASIBLE_POINT,
35-
MOI.INFEASIBILITY_CERTIFICATE)
36-
)
37-
MOIT.lin4test(bridged_mock, config)
38-
39-
@test MOI.get(mock, MOI.NumberOfVariables()) == 1
40-
@test length(MOI.get(mock, MOI.ListOfVariableIndices())) == 1
41-
@test first(MOI.get(mock, MOI.ListOfVariableIndices())).value 0
42-
@test MOI.get(bridged_mock, MOI.NumberOfVariables()) == 1
43-
vis = MOI.get(bridged_mock, MOI.ListOfVariableIndices())
44-
@test vis == [MOI.VariableIndex(-1)]
45-
test_delete_bridged_variable(bridged_mock, vis[1], MOI.Nonpositives, 1, (
46-
(MOI.VectorOfVariables, MOI.Nonnegatives, 0),
47-
))
17+
@testset "lin2v" begin
18+
mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [-4, 3, 16, 0],
19+
(MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[7, 2, -4]])
20+
MOIT.lin2vtest(bridged_mock, config)
21+
22+
@test MOI.get(mock, MOI.NumberOfVariables()) == 4
23+
@test MOI.get(bridged_mock, MOI.NumberOfVariables()) == 4
24+
vis = MOI.get(bridged_mock, MOI.ListOfVariableIndices())
25+
y = vis[4]
26+
@test y.value == -1
27+
28+
@test MOI.supports(bridged_mock, MOI.VariablePrimalStart(), MOI.VariableIndex)
29+
MOI.set(bridged_mock, MOI.VariablePrimalStart(), y, 1.0)
30+
x, y_flipped, z, s = MOI.get(mock, MOI.ListOfVariableIndices())
31+
@test MOI.get(mock, MOI.VariablePrimalStart(), y_flipped) == -1
32+
end
33+
34+
@testset "lin4" begin
35+
MOIU.set_mock_optimize!(mock,
36+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
37+
mock, MOI.INFEASIBLE, MOI.INFEASIBLE_POINT,
38+
MOI.INFEASIBILITY_CERTIFICATE)
39+
)
40+
MOIT.lin4test(bridged_mock, config)
41+
42+
@test MOI.get(mock, MOI.NumberOfVariables()) == 1
43+
@test length(MOI.get(mock, MOI.ListOfVariableIndices())) == 1
44+
@test first(MOI.get(mock, MOI.ListOfVariableIndices())).value 0
45+
@test MOI.get(bridged_mock, MOI.NumberOfVariables()) == 1
46+
vis = MOI.get(bridged_mock, MOI.ListOfVariableIndices())
47+
@test vis == [MOI.VariableIndex(-1)]
48+
test_delete_bridged_variable(bridged_mock, vis[1], MOI.Nonpositives, 1, (
49+
(MOI.VectorOfVariables, MOI.Nonnegatives, 0),
50+
))
51+
end
52+
53+
@testset "Delete in vector" begin
54+
MOI.empty!(bridged_mock)
55+
vis, ci = MOI.add_constrained_variables(bridged_mock, MOI.Nonpositives(4))
56+
test_delete_bridged_variable(bridged_mock, vis[2], MOI.Nonpositives, 4, (
57+
(MOI.VectorOfVariables, MOI.Nonnegatives, 0),
58+
), used_bridges = 0, used_constraints = 0)
59+
end
4860
end

test/Bridges/Variable/free.jl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,22 @@ end
8989
xa, xb, ya, yb = MOI.get(mock, MOI.ListOfVariableIndices())
9090
@test MOI.get(mock, MOI.VariablePrimalStart(), [xa, xb, ya, yb]) == [1.0, 0.0, 0.0, -1.0]
9191
end
92+
93+
@testset "Linear11" begin
94+
MOIU.set_mock_optimize!(mock,
95+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 0.0, 1.0, 0.0]),
96+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [0.5, 0.0, 0.5, 0.0]))
97+
MOIT.linear11test(bridged_mock, config)
98+
99+
vis = MOI.get(bridged_mock, MOI.ListOfVariableIndices())
100+
@test vis == MOI.VariableIndex.([-1, -2])
101+
102+
test_delete_bridged_variable(bridged_mock, vis[1], MOI.Reals, 2, (
103+
(MOI.VectorOfVariables, MOI.Nonnegatives, 0),
104+
(MOI.VectorOfVariables, MOI.Nonpositives, 0)
105+
), used_bridges = 0, used_constraints = 0)
106+
test_delete_bridged_variable(bridged_mock, vis[2], MOI.Reals, 1, (
107+
(MOI.VectorOfVariables, MOI.Nonnegatives, 0),
108+
(MOI.VectorOfVariables, MOI.Nonpositives, 0)
109+
))
110+
end

0 commit comments

Comments
 (0)