Skip to content

Commit 3da3dd5

Browse files
authored
Fix MOI.ConstraintName (#323)
1 parent 9ef4efb commit 3da3dd5

File tree

3 files changed

+91
-31
lines changed

3 files changed

+91
-31
lines changed

src/MOI_wrapper.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
3434
Nothing,
3535
Dict{String,Union{Nothing,MOI.VariableIndex}},
3636
}
37+
name_to_constraint_index::Union{
38+
Nothing,
39+
Dict{String,Union{Nothing,MOI.ConstraintIndex}},
40+
}
3741

3842
function Optimizer(; kwargs...)
3943
o = new(
@@ -50,6 +54,7 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
5054
MOI.COMPUTE_CONFLICT_NOT_CALLED,
5155
_kSCIP_SOLVE_STATUS_NOT_CALLED,
5256
nothing,
57+
nothing,
5358
)
5459
# Set all parameters given as keyword arguments, replacing the
5560
# delimiter, since "/" is used by all SCIP parameters, but is not
@@ -94,6 +99,7 @@ function MOI.empty!(o::Optimizer)
9499
o.moi_heuristic = nothing
95100
o.scip_solve_status = _kSCIP_SOLVE_STATUS_NOT_CALLED
96101
o.name_to_variable = nothing
102+
o.name_to_constraint_index = nothing
97103
return nothing
98104
end
99105

@@ -418,6 +424,7 @@ function MOI.delete(o::Optimizer, ci::MOI.ConstraintIndex{F,S}) where {F,S}
418424
end
419425
delete!(o.reference, cons(o, ci))
420426
delete(o.inner, ConsRef(ci.value))
427+
o.name_to_constraint_index = nothing
421428
return nothing
422429
end
423430

src/MOI_wrapper/constraints.jl

Lines changed: 84 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,52 @@
33
# Use of this source code is governed by an MIT-style license that can be found
44
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
55

6-
# FIXME(odow): ConstraintName is missing MOI.supports, doesn't account for
7-
# duplicate names, and doesn't error properly for MOI.VariableIndex constraints.
6+
function MOI.supports(
7+
::Optimizer,
8+
::MOI.ConstraintName,
9+
::Type{MOI.ConstraintIndex{F,S}},
10+
) where {
11+
F<:Union{
12+
MOI.ScalarAffineFunction{Float64},
13+
MOI.ScalarQuadraticFunction{Float64},
14+
MOI.VectorAffineFunction{Float64},
15+
MOI.VectorOfVariables,
16+
},
17+
S,
18+
}
19+
return true
20+
end
21+
822
function MOI.get(
923
o::Optimizer,
1024
::MOI.ConstraintName,
11-
ci::MOI.ConstraintIndex,
12-
)::String
13-
return GC.@preserve o unsafe_string(SCIPconsGetName(cons(o, ci)))
25+
ci::MOI.ConstraintIndex{F},
26+
)::String where {
27+
F<:Union{
28+
MOI.ScalarAffineFunction{Float64},
29+
MOI.ScalarQuadraticFunction{Float64},
30+
MOI.VectorAffineFunction{Float64},
31+
MOI.VectorOfVariables,
32+
},
33+
}
34+
return unsafe_string(SCIPconsGetName(cons(o, ci)))
1435
end
1536

1637
function MOI.set(
1738
o::Optimizer,
1839
::MOI.ConstraintName,
19-
ci::MOI.ConstraintIndex,
40+
ci::MOI.ConstraintIndex{F},
2041
name::String,
21-
)
42+
) where {
43+
F<:Union{
44+
MOI.ScalarAffineFunction{Float64},
45+
MOI.ScalarQuadraticFunction{Float64},
46+
MOI.VectorAffineFunction{Float64},
47+
MOI.VectorOfVariables,
48+
},
49+
}
2250
@SCIP_CALL SCIPchgConsName(o, cons(o, ci), name)
51+
o.name_to_constraint_index = nothing
2352
return nothing
2453
end
2554

@@ -32,6 +61,54 @@ function MOI.set(
3261
return throw(MOI.VariableIndexConstraintNameError())
3362
end
3463

64+
function _rebuild_name_to_constraint_index(o::Optimizer)
65+
o.name_to_constraint_index =
66+
Dict{String,Union{Nothing,MOI.ConstraintIndex}}()
67+
for ((F, S), set) in o.constypes
68+
if F <: MOI.VariableIndex
69+
continue
70+
end
71+
for cref in set
72+
ci = MOI.ConstraintIndex{F,S}(cref.val)
73+
name = MOI.get(o, MOI.ConstraintName(), ci)
74+
if isempty(name)
75+
continue
76+
elseif haskey(o.name_to_constraint_index, name)
77+
o.name_to_constraint_index[name] = nothing # Duplicate name
78+
else
79+
o.name_to_constraint_index[name] = ci
80+
end
81+
end
82+
end
83+
return
84+
end
85+
86+
function MOI.get(o::Optimizer, ::Type{MOI.ConstraintIndex}, name::String)
87+
if o.name_to_constraint_index === nothing
88+
_rebuild_name_to_constraint_index(o)
89+
end
90+
ci = get(o.name_to_constraint_index, name, missing)
91+
if ismissing(ci)
92+
return nothing
93+
elseif isnothing(ci)
94+
error("Duplicate name")
95+
else
96+
return ci
97+
end
98+
end
99+
100+
function MOI.get(
101+
o::Optimizer,
102+
::Type{MOI.ConstraintIndex{F,S}},
103+
name::String,
104+
) where {F,S}
105+
ci = MOI.get(o, MOI.ConstraintIndex, name)
106+
if !(ci isa MOI.ConstraintIndex{F,S})
107+
return nothing
108+
end
109+
return ci
110+
end
111+
35112
function MOI.is_valid(o::Optimizer, c::MOI.ConstraintIndex{F,S}) where {F,S}
36113
cons_set = get(o.constypes, (F, S), nothing)
37114
if cons_set === nothing
@@ -42,20 +119,3 @@ function MOI.is_valid(o::Optimizer, c::MOI.ConstraintIndex{F,S}) where {F,S}
42119
end
43120
return haskey(o.inner.conss, SCIP.ConsRef(c.value))
44121
end
45-
46-
function MOI.get(o::Optimizer, ::Type{MOI.ConstraintIndex}, name::String)
47-
ptr = SCIPfindCons(o, name)
48-
if ptr == C_NULL
49-
return nothing
50-
end
51-
cref = get(o.reference, ptr, nothing)
52-
if cref === nothing
53-
return cref
54-
end
55-
for ((F, S), setref) in o.constypes
56-
if cref in setref
57-
return MOI.ConstraintIndex{F,S}(cref.val)
58-
end
59-
end
60-
return error("Constraint type not found for constraint $cref")
61-
end

test/MOI_tests.jl

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ function test_runtests_bridged()
6666
rtol=1e-4,
6767
exclude=Any[
6868
MOI.ConstraintDual,
69-
MOI.ConstraintName,
7069
MOI.DualObjectiveValue,
7170
MOI.VariableBasisStatus,
7271
MOI.ConstraintBasisStatus,
@@ -76,10 +75,8 @@ function test_runtests_bridged()
7675
exclude=[
7776
# TODO(odow): bugs to fix
7877
r"^test_model_delete$",
79-
r"^test_model_ScalarAffineFunction_ConstraintName$",
8078
r"^test_model_LowerBoundAlreadySet$",
8179
r"^test_model_UpperBoundAlreadySet$",
82-
r"^test_model_duplicate_ScalarAffineFunction_ConstraintName$",
8380
# SCIP does not support nonlinear objective functions.
8481
r"^test_nonlinear_hs071_NLPBlockDual$",
8582
r"^test_nonlinear_invalid$",
@@ -100,7 +97,6 @@ function test_runtests_direct()
10097
rtol=1e-4,
10198
exclude=Any[
10299
MOI.ConstraintDual,
103-
MOI.ConstraintName,
104100
MOI.DualObjectiveValue,
105101
MOI.VariableBasisStatus,
106102
MOI.ConstraintBasisStatus,
@@ -109,10 +105,8 @@ function test_runtests_direct()
109105
warn_unsupported=false,
110106
exclude=[
111107
# TODO(odow): bugs to fix
112-
r"^test_model_ScalarAffineFunction_ConstraintName$",
113108
r"^test_model_LowerBoundAlreadySet$",
114109
r"^test_model_UpperBoundAlreadySet$",
115-
r"^test_model_duplicate_ScalarAffineFunction_ConstraintName$",
116110
# SCIP does not support nonlinear objective functions.
117111
r"^test_nonlinear_hs071_NLPBlockDual$",
118112
r"^test_nonlinear_invalid$",
@@ -672,7 +666,6 @@ function _test_broken_indicator_test(presolving)
672666
rtol=1e-4,
673667
exclude=Any[
674668
MOI.ConstraintDual,
675-
MOI.ConstraintName,
676669
MOI.DualObjectiveValue,
677670
MOI.VariableBasisStatus,
678671
MOI.ConstraintBasisStatus,

0 commit comments

Comments
 (0)