Skip to content

Commit fe15ea9

Browse files
authored
Fix deleting VectorAffineFunction constraints (#205)
1 parent 4009298 commit fe15ea9

File tree

3 files changed

+37
-50
lines changed

3 files changed

+37
-50
lines changed

src/MosekTools.jl

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,6 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
107107
# and Mosek.getvarbound so we need to keep them here so implement `MOI.is_valid`
108108
x_constraints::Vector{UInt8}
109109

110-
F_rows::Dict{Int,UnitRange{Int}} # TODO can it be obtained from Mosek ?
111-
112110
# The total length of `x_block` matches the number of variables in
113111
# the underlying task, and the number of blocks corresponds to the
114112
# number variables allocated in the Model.
@@ -166,7 +164,6 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
166164
Dict{MOI.ConstraintIndex,String}(), # con_to_name
167165
nothing, # name_to_con
168166
UInt8[], # x_constraints
169-
Dict{Int,UnitRange{Int}}(), # F_rows
170167
LinkedInts(), # x_block
171168
MatrixIndex[], # x_sd
172169
Int[], # sd_dim
@@ -493,8 +490,7 @@ function MOI.is_empty(m::Optimizer)
493490
return Mosek.getnumvar(m.task) == 0 &&
494491
Mosek.getnumcon(m.task) == 0 &&
495492
Mosek.getnumcone(m.task) == 0 &&
496-
Mosek.getnumbarvar(m.task) == 0 &&
497-
isempty(m.F_rows)
493+
Mosek.getnumbarvar(m.task) == 0
498494
end
499495

500496
# MOI.empty!
@@ -519,7 +515,6 @@ function MOI.empty!(model::Optimizer)
519515
model.name_to_variable = nothing
520516
empty!(model.con_to_name)
521517
model.name_to_con = nothing
522-
empty!(model.F_rows)
523518
empty!(model.x_constraints)
524519
model.x_block = LinkedInts()
525520
empty!(model.x_sd)

src/attributes.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -548,9 +548,9 @@ function MOI.get!(
548548
ci::MOI.ConstraintIndex{MOI.VectorAffineFunction{Float64},D},
549549
) where {D}
550550
MOI.check_result_index_bounds(m, attr)
551-
r = rows(m, ci)
551+
afeidxs = Mosek.getaccafeidxlist(m.task, ci.value)
552552
idx = reorder(1:length(output), D, true)
553-
output[idx] = _dual_scale(m) * m.solutions[attr.result_index].doty[r]
553+
output[idx] = _dual_scale(m) * m.solutions[attr.result_index].doty[afeidxs]
554554
return
555555
end
556556

@@ -572,7 +572,7 @@ function solsize(
572572
m::Optimizer,
573573
ci::MOI.ConstraintIndex{MOI.VectorAffineFunction{Float64}},
574574
)
575-
return length(rows(m, ci))
575+
return Mosek.getaccn(m.task, ci.value)
576576
end
577577

578578
function solsize(

src/constraint.jl

Lines changed: 33 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -348,16 +348,6 @@ function columns(m::Optimizer, ci::MOI.ConstraintIndex{MOI.VectorOfVariables})
348348
return ColumnIndices(Mosek.getcone(m.task, coneidx)[4])
349349
end
350350

351-
function rows(
352-
m::Optimizer,
353-
ci::MOI.ConstraintIndex{MOI.VectorAffineFunction{Float64}},
354-
)
355-
if !(ci.value in keys(m.F_rows))
356-
throw(MOI.InvalidIndex(ci))
357-
end
358-
return m.F_rows[ci.value]
359-
end
360-
361351
function _append_cone_domain(t::Mosek.Task, n::Int, ::MOI.ExponentialCone)
362352
@assert n == 3
363353
return Mosek.appendprimalexpconedomain(t)
@@ -624,7 +614,6 @@ function MOI.add_constraint(
624614
afei = Mosek.getnumafe(m.task)
625615
b = -reorder(axbs.constants, D, true)
626616
domi = _append_cone_domain(m.task, length(axbs.constants), dom)
627-
m.F_rows[acci] = afei .+ eachindex(b)
628617
Mosek.appendafes(m.task, length(axbs.constants))
629618
Mosek.appendaccseq(m.task, domi, afei + 1, b)
630619
rsubi, rsubj, rcof = Int64[], Int32[], Float64[]
@@ -785,23 +774,24 @@ function MOI.get(
785774
attr::MOI.ConstraintFunction,
786775
ci::MOI.ConstraintIndex{MOI.VectorAffineFunction{Float64},S},
787776
) where {S<:VectorConeDomain}
777+
MOI.throw_if_not_valid(m, ci)
788778
if length(m.sd_dim) > 0
789779
# Cannot get function if there are matrix variables
790780
throw(MOI.GetAttributeNotAllowed(attr))
791781
end
792-
r = rows(m, ci)
793-
frow, fcol, fval = Mosek.getaccftrip(m.task)
794-
constants = Mosek.getaccb(m.task, ci.value)
795-
set = MOI.Utilities.set_with_dimension(S, length(r))
782+
rows = Mosek.getaccafeidxlist(m.task, ci.value)
783+
set = MOI.Utilities.set_with_dimension(S, length(rows))
796784
terms = MOI.VectorAffineTerm{Float64}[]
797-
for (frowi, fcoli, fvali) in zip(frow, fcol, fval)
798-
if frowi in r
799-
row = reorder(frowi - first(r) + 1, set, false)
800-
term = MOI.ScalarAffineTerm(fvali, _col_to_index(m, fcoli))
801-
push!(terms, MOI.VectorAffineTerm(row, term))
785+
for (i, row) in enumerate(rows)
786+
numnz, varidx, val = Mosek.getafefrow(m.task, row)
787+
output_index = reorder(i, set, false)
788+
for (coli, vali) in zip(varidx, val)
789+
term = MOI.ScalarAffineTerm(vali, _col_to_index(m, coli))
790+
push!(terms, MOI.VectorAffineTerm(output_index, term))
802791
end
803792
end
804-
return MOI.VectorAffineFunction(terms, -reorder(constants, S, false))
793+
constants = -reorder(Mosek.getaccb(m.task, ci.value), S, false)
794+
return MOI.VectorAffineFunction(terms, constants)
805795
end
806796

807797
function _type_cone(ct)
@@ -948,26 +938,28 @@ function MOI.is_valid(
948938
return Mosek.getdomaintype(model.task, domidx) == _domain(S)
949939
end
950940

951-
# Commenting out as it doesn't work (gives a MethodError)
952-
953-
# Deleting a constraint block means clearing non-zeros from the its
954-
# AFE rows and resetting the underlying ACC to an empty domain. We do
955-
# not reclaim the AFEs.
956-
# function MOI.delete(m::Optimizer,
957-
# cref::MOI.ConstraintIndex{F,D}) where {F <: MOI.VectorAffineFunction{Float64},
958-
# D <: VectorConeDomain}
959-
# MOI.throw_if_not_valid(m, cref)
960-
# Mosek.putaccname(m.task,cref.value,"")
961-
# afeidxs = Mosek.getaccafeidxlist(m.task,cref.value)
962-
# # clear afe non-zeros, but don't delete or reuse afe idx
963-
# # FIXME gives a MethodError
964-
# Mosek.putafefrowlist(afeidxs,zeros(Int32,length(afeidxs)),zeros(Int64,length(afeidxs)),Int32[],Float64[])
965-
# putaccdom(m.task,
966-
# cref.value,
967-
# 1, # the empty zero domain,
968-
# Int64[],
969-
# Float64[])
970-
# end
941+
# Deleting a constraint block means clearing non-zeros from the its AFE rows and
942+
# resetting the underlying ACC to an empty domain. We do not reclaim the AFEs.
943+
function MOI.delete(
944+
m::Optimizer,
945+
cref::MOI.ConstraintIndex{MOI.VectorAffineFunction{Float64},S},
946+
) where {S<:VectorConeDomain}
947+
MOI.throw_if_not_valid(m, cref)
948+
Mosek.putaccname(m.task, cref.value, "")
949+
afeidxs = Mosek.getaccafeidxlist(m.task, cref.value)
950+
Mosek.putafefrowlist(
951+
m.task,
952+
afeidxs,
953+
zeros(Int32, length(afeidxs)),
954+
ones(Int64, length(afeidxs)),
955+
Int32[],
956+
Float64[],
957+
)
958+
Mosek.putacc(m.task, cref.value, Mosek.MSK_DOMAIN_RZERO, Int64[], Float64[])
959+
delete!(m.con_to_name, cref)
960+
m.name_to_con = nothing
961+
return
962+
end
971963

972964
function MOI.is_valid(
973965
model::Optimizer,

0 commit comments

Comments
 (0)