Skip to content

Commit 4524657

Browse files
committed
wrapup conflicts
1 parent b3d1020 commit 4524657

File tree

3 files changed

+96
-25
lines changed

3 files changed

+96
-25
lines changed

src/Utilities/mockoptimizer.jl

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,10 @@ function MockOptimizer(
126126
false,
127127
MOI.COMPUTE_CONFLICT_NOT_CALLED,
128128
0,
129-
Dict{MOI.ConstraintIndex,Dict{Int,MOI.ConflictParticipationStatusCode}}(),
129+
Dict{
130+
MOI.ConstraintIndex,
131+
Dict{Int,MOI.ConflictParticipationStatusCode},
132+
}(),
130133
# Basis status
131134
Dict{MOI.ConstraintIndex,Dict{Int,MOI.BasisStatusCode}}(),
132135
Dict{MOI.VariableIndex,Dict{Int,MOI.BasisStatusCode}}(),
@@ -304,6 +307,13 @@ function MOI.set(
304307
return
305308
end
306309

310+
MOI.get(mock::MockOptimizer, ::MOI.ConflictCount) = mock.conflict_count
311+
312+
function MOI.set(mock::MockOptimizer, ::MOI.ConflictCount, x)
313+
mock.conflict_count = x
314+
return
315+
end
316+
307317
function MOI.set(
308318
mock::MockOptimizer,
309319
::MOI.ConflictStatus,
@@ -456,7 +466,6 @@ function MOI.set(
456466
value,
457467
)
458468
_safe_set_result(mock.constraint_conflict_status, attr, idx, value)
459-
# mock.constraint_conflict_status[xor_index(idx)] = value
460469
return
461470
end
462471

@@ -729,11 +738,10 @@ function MOI.get(
729738
attr::MOI.ConstraintConflictStatus,
730739
idx::MOI.ConstraintIndex,
731740
)
732-
MOI.check_result_index_bounds(mock, attr)
741+
MOI.check_conflict_index_bounds(mock, attr)
733742
MOI.throw_if_not_valid(mock, idx)
734-
# return mock.constraint_conflict_status[xor_index(idx)]
735743
return _safe_get_result(
736-
mock.variable_basis_status,
744+
mock.constraint_conflict_status,
737745
attr,
738746
idx,
739747
"conflict status",
@@ -750,6 +758,9 @@ function _safe_set_result(
750758
if !haskey(dict, xored)
751759
dict[xored] = V()
752760
end
761+
if hasproperty(attr, :conflict_index)
762+
return dict[xored][attr.conflict_index] = value
763+
end
753764
return dict[xored][attr.result_index] = value
754765
end
755766

@@ -764,11 +775,16 @@ function _safe_get_result(
764775
if result_to_value === nothing
765776
error("No mock $name is set for ", index_name, " `", index, "`.")
766777
end
767-
value = get(result_to_value, attr.result_index, nothing)
778+
data_index = if hasproperty(attr, :conflict_index)
779+
attr.conflict_index
780+
else
781+
attr.result_index
782+
end
783+
value = get(result_to_value, data_index, nothing)
768784
if value === nothing
769785
error(
770786
"No mock $name is set for $(index_name) `$(index)` at result " *
771-
"index `$(attr.result_index)`.",
787+
"index `$(data_index)`.",
772788
)
773789
end
774790
return value
@@ -1066,24 +1082,16 @@ function mock_optimize!(
10661082
for ((F, S), result) in constraint_basis_status
10671083
indices = MOI.get(mock, MOI.ListOfConstraintIndices{F,S}())
10681084
for (i, ci) in enumerate(indices)
1069-
MOI.set(
1070-
mock,
1071-
MOI.ConstraintBasisStatus(),
1072-
ci,
1073-
result,
1074-
)
1085+
MOI.set(mock, MOI.ConstraintBasisStatus(), ci, result[i])
10751086
end
10761087
end
1077-
for con_conflict_pair in constraint_conflict_status
1078-
F, S = con_conflict_pair.first
1088+
if length(constraint_conflict_status) > 0
1089+
MOI.set(mock, MOI.ConflictCount(), 1)
1090+
end
1091+
for ((F, S), result) in constraint_conflict_status
10791092
indices = MOI.get(mock, MOI.ListOfConstraintIndices{F,S}())
10801093
for (i, ci) in enumerate(indices)
1081-
MOI.set(
1082-
mock,
1083-
MOI.ConstraintConflictStatus(),
1084-
ci,
1085-
con_conflict_pair.second[i],
1086-
)
1094+
MOI.set(mock, MOI.ConstraintConflictStatus(), ci, result[i])
10871095
end
10881096
end
10891097
if length(variable_basis_status) > 0

src/attributes.jl

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,48 @@ function Base.showerror(io::IO, err::ResultIndexBoundsError)
217217
)
218218
end
219219

220+
"""
221+
struct ConflictIndexBoundsError{AttrType} <: Exception
222+
attr::AttrType
223+
conflict_count::Int
224+
end
225+
226+
An error indicating that the requested attribute `attr` could not be retrieved,
227+
because the solver returned too few conflicts compared to what was requested.
228+
For instance, the user tries to retrieve `ConstraintConflictStatus(2)` when
229+
only one conflict is available, or when the model is feasible.
230+
231+
See also: [`check_conflict_index_bounds`](@ref).
232+
"""
233+
struct ConflictIndexBoundsError{AttrType} <: Exception
234+
attr::AttrType
235+
conflict_count::Int
236+
end
237+
238+
"""
239+
check_conflict_index_bounds(model::ModelLike, attr)
240+
241+
This function checks whether enough conflicts are available in the `model` for
242+
the requested `attr`, using its `conflict_index` field. If the model
243+
does not have sufficient conflicts to answer the query, it throws a
244+
[`ConflictIndexBoundsError`](@ref).
245+
"""
246+
function check_conflict_index_bounds(model::ModelLike, attr)
247+
conflict_count = get(model, ConflictCount())
248+
if !(1 <= attr.conflict_index <= conflict_count)
249+
throw(ConflictIndexBoundsError(attr, conflict_count))
250+
end
251+
return
252+
end
253+
254+
function Base.showerror(io::IO, err::ConflictIndexBoundsError)
255+
return print(
256+
io,
257+
"Conflict index of attribute $(err.attr) out of bounds. There are " *
258+
"currently $(err.conflict_count) conflict(s) in the model.",
259+
)
260+
end
261+
220262
"""
221263
supports(model::ModelLike, sub::AbstractSubmittable)::Bool
222264
@@ -2716,10 +2758,13 @@ function attribute_value_type(::ConstraintConflictStatus)
27162758
return ConflictParticipationStatusCode
27172759
end
27182760

2719-
function check_result_index_bounds(model::ModelLike, attr::ConstraintConflictStatus)
2720-
result_count = get(model, ConflictCount())
2721-
if !(1 <= attr.result_index <= result_count)
2722-
throw(ResultIndexBoundsError(attr, result_count))
2761+
function check_conflict_index_bounds(
2762+
model::ModelLike,
2763+
attr::ConstraintConflictStatus,
2764+
)
2765+
conflict_count = get(model, ConflictCount())
2766+
if !(1 <= attr.conflict_index <= conflict_count)
2767+
throw(ConflictIndexBoundsError(attr, conflict_count))
27232768
end
27242769
return
27252770
end
@@ -3273,6 +3318,7 @@ function is_set_by_optimize(
32733318
RawSolver,
32743319
ResultCount,
32753320
ConflictStatus,
3321+
ConflictCount,
32763322
ConstraintConflictStatus,
32773323
TerminationStatus,
32783324
RawStatusString,

test/General/attributes.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ function test_attributes_integration_compute_conflict_1()
7171
@test MOI.get(optimizer, MOI.ConflictStatus()) ==
7272
MOI.COMPUTE_CONFLICT_NOT_CALLED
7373
MOI.set(optimizer, MOI.ConflictStatus(), MOI.CONFLICT_FOUND)
74+
MOI.set(optimizer, MOI.ConflictCount(), 1)
7475
MOI.set(
7576
optimizer,
7677
MOI.ConstraintConflictStatus(),
@@ -97,9 +98,23 @@ function test_attributes_integration_compute_conflict_1()
9798
)
9899
MOI.compute_conflict!(model)
99100
@test MOI.get(model, MOI.ConflictStatus()) == MOI.CONFLICT_FOUND
101+
@test MOI.get(model, MOI.ConflictCount()) == 1
100102
@test MOI.get(model, MOI.ConstraintConflictStatus(), c1) ==
101103
MOI.NOT_IN_CONFLICT
102104
@test MOI.get(model, MOI.ConstraintConflictStatus(), c2) == MOI.IN_CONFLICT
105+
@test MOI.get(model, MOI.ConstraintConflictStatus(1), c1) ==
106+
MOI.NOT_IN_CONFLICT
107+
@test MOI.get(model, MOI.ConstraintConflictStatus(1), c2) == MOI.IN_CONFLICT
108+
@test_throws MOI.ConflictIndexBoundsError MOI.get(
109+
model,
110+
MOI.ConstraintConflictStatus(2),
111+
c1,
112+
)
113+
@test_throws MOI.ConflictIndexBoundsError MOI.get(
114+
model,
115+
MOI.ConstraintConflictStatus(2),
116+
c2,
117+
)
103118
end
104119

105120
MOI.Utilities.@model(
@@ -127,8 +142,10 @@ function test_attributes_integration_compute_conflict_2()
127142
@test MOI.get(optimizer, MOI.ConflictStatus()) ==
128143
MOI.COMPUTE_CONFLICT_NOT_CALLED
129144
MOI.set(optimizer, MOI.ConflictStatus(), MOI.CONFLICT_FOUND)
145+
MOI.set(optimizer, MOI.ConflictCount(), 1)
130146
MOI.compute_conflict!(model)
131147
@test MOI.get(model, MOI.ConflictStatus()) == MOI.CONFLICT_FOUND
148+
@test MOI.get(model, MOI.ConflictCount()) == 1
132149
@test_throws ArgumentError MOI.get(model, MOI.ConstraintConflictStatus(), c)
133150
end
134151

0 commit comments

Comments
 (0)