Skip to content

Commit 9e842ea

Browse files
authored
Improve error messages of various errors (#3036)
1 parent adecb94 commit 9e842ea

8 files changed

Lines changed: 219 additions & 80 deletions

File tree

src/Bridges/lazy_bridge_optimizer.jl

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,22 @@ function is_variable_bridged(b::LazyBridgeOptimizer, S::Type{<:MOI.AbstractSet})
544544
return is_bridged(b, S) && is_variable_edge_best(b.graph, node(b, S))
545545
end
546546

547+
function MOI.UnsupportedConstraint{F,S}(
548+
model::LazyBridgeOptimizer,
549+
) where {F<:MOI.AbstractFunction,S<:MOI.AbstractSet}
550+
msg = """
551+
## More information
552+
553+
Bridges are enabled, but there are none that can rewrite the \
554+
constraint into a form supported by the solver.
555+
556+
The model that we were unable to add the constraint to is:
557+
558+
$(sprint(show, model))
559+
"""
560+
return MOI.UnsupportedConstraint{F,S}(msg)
561+
end
562+
547563
function bridge_type(b::LazyBridgeOptimizer, S::Type{<:MOI.AbstractSet})
548564
bt = get(b.cached_bridge_type, (S,), nothing)
549565
if bt !== nothing
@@ -552,7 +568,7 @@ function bridge_type(b::LazyBridgeOptimizer, S::Type{<:MOI.AbstractSet})
552568
index = bridge_index(b, S)
553569
if iszero(index)
554570
F = MOI.Utilities.variable_function_type(S)
555-
throw(MOI.UnsupportedConstraint{F,S}())
571+
throw(MOI.UnsupportedConstraint{F,S}(b))
556572
end
557573
new_bt = Variable.concrete_bridge_type(b.variable_bridge_types[index], S)
558574
b.cached_bridge_type[(S,)] = new_bt
@@ -570,7 +586,7 @@ function bridge_type(
570586
end
571587
index = bridge_index(b, F, S)
572588
if iszero(index)
573-
throw(MOI.UnsupportedConstraint{F,S}())
589+
throw(MOI.UnsupportedConstraint{F,S}(b))
574590
end
575591
new_bt =
576592
Constraint.concrete_bridge_type(b.constraint_bridge_types[index], F, S)

src/attributes.jl

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,11 @@ end
243243
function Base.showerror(io::IO, err::ResultIndexBoundsError)
244244
return print(
245245
io,
246-
"Result index of attribute $(err.attr) out of bounds. There are " *
247-
"currently $(err.result_count) solution(s) in the model.",
246+
"""
247+
Result index of attribute $(err.attr) is out of bounds.
248+
249+
There are currently $(err.result_count) solution(s) in the model.
250+
""",
248251
)
249252
end
250253

@@ -285,8 +288,11 @@ end
285288
function Base.showerror(io::IO, err::ConflictIndexBoundsError)
286289
return print(
287290
io,
288-
"Conflict index of attribute $(err.attr) out of bounds. There are " *
289-
"currently $(err.conflict_count) conflict(s) in the model.",
291+
"""
292+
Conflict index of attribute $(err.attr) is out of bounds.
293+
294+
There are currently $(err.conflict_count) conflict(s) in the model.
295+
""",
290296
)
291297
end
292298

@@ -1261,9 +1267,13 @@ end
12611267
function Base.showerror(io::IO, err::OptimizeInProgress)
12621268
return print(
12631269
io,
1264-
typeof(err),
1265-
": Cannot get result as the `MOI.optimize!` has not",
1266-
" finished.",
1270+
"""
1271+
Unable to get the attribute $(err.attr) because `MOI.optimize!` in progress.
1272+
1273+
This error occurs when you try to query an unsupported attribute from \
1274+
inside a callback. Consult the solver's documentation to learn which \
1275+
attributes may be queried inside a callback.
1276+
""",
12671277
)
12681278
end
12691279

@@ -2633,8 +2643,15 @@ struct FunctionTypeMismatch{F1,F2} <: Exception end
26332643
function Base.showerror(io::IO, err::FunctionTypeMismatch{F1,F2}) where {F1,F2}
26342644
return print(
26352645
io,
2636-
"""$(typeof(err)): Cannot modify functions of different types.
2637-
Constraint type is $F1 while the replacement function is of type $F2.""",
2646+
"""
2647+
Cannot modify functions of different types.
2648+
2649+
The original function is a $F1
2650+
2651+
The replacement function is a $F2
2652+
2653+
When modifying the `ConstraintFunction` the types must be the same.
2654+
""",
26382655
)
26392656
end
26402657

@@ -2714,9 +2731,16 @@ struct SetTypeMismatch{S1,S2} <: Exception end
27142731
function Base.showerror(io::IO, err::SetTypeMismatch{S1,S2}) where {S1,S2}
27152732
return print(
27162733
io,
2717-
"""$(typeof(err)): Cannot modify sets of different types. Constraint
2718-
type is $S1 while the replacement set is of type $S2. Use `transform`
2719-
instead.""",
2734+
"""
2735+
Cannot modify sets of different types.
2736+
2737+
The original set is a $S1
2738+
2739+
The replacement set is a $S2
2740+
2741+
When modifying the `ConstraintSet` the types must be the same.
2742+
Alternatively, you may have meant to call `MOI.transform` instead.
2743+
""",
27202744
)
27212745
end
27222746

src/constraints.jl

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,35 +35,45 @@ the model, that is, that [`supports_constraint`](@ref) returns `false`.
3535
3636
```jldoctest
3737
julia> showerror(stdout, MOI.UnsupportedConstraint{MOI.VariableIndex,MOI.ZeroOne}())
38-
UnsupportedConstraint: `MathOptInterface.VariableIndex`-in-`MathOptInterface.ZeroOne` constraints are not supported by the
39-
solver you have chosen, and we could not reformulate your model into a
40-
form that is supported.
38+
UnsupportedConstraint{
39+
MathOptInterface.VariableIndex,
40+
MathOptInterface.ZeroOne,
41+
}
4142
42-
To fix this error you must choose a different solver.
43+
This constraint type is not supported by the solver.
4344
45+
To fix this error you must choose a different solver.
4446
```
4547
"""
4648
struct UnsupportedConstraint{F<:AbstractFunction,S<:AbstractSet} <:
4749
UnsupportedError
4850
# Human-friendly explanation why the attribute cannot be set
4951
message::String
50-
end
5152

52-
UnsupportedConstraint{F,S}() where {F,S} = UnsupportedConstraint{F,S}("")
53+
function UnsupportedConstraint{F,S}(
54+
msg::String = "",
55+
) where {F<:AbstractFunction,S<:AbstractSet}
56+
return new{F,S}(msg)
57+
end
58+
end
5359

5460
function Base.showerror(io::IO, err::UnsupportedConstraint{F,S}) where {F,S}
5561
print(
5662
io,
5763
"""
58-
UnsupportedConstraint: `$F`-in-`$S` constraints are not supported by the
59-
solver you have chosen, and we could not reformulate your model into a
60-
form that is supported.
64+
UnsupportedConstraint{
65+
$F,
66+
$S,
67+
}
6168
62-
To fix this error you must choose a different solver.
69+
This constraint type is not supported by the solver.
6370
64-
$(err.message)
71+
To fix this error you must choose a different solver.
6572
""",
6673
)
74+
if !isempty(err.message)
75+
print(io, "\n", err.message)
76+
end
6777
return
6878
end
6979

@@ -289,10 +299,16 @@ end
289299
function Base.showerror(io::IO, err::LowerBoundAlreadySet{S1,S2}) where {S1,S2}
290300
return print(
291301
io,
292-
typeof(err),
293-
": Cannot add `VariableIndex`-in-`$(S2)` constraint for variable ",
294-
"$(err.vi) as a `VariableIndex`-in-`$(S1)` constraint was already ",
295-
"set for this variable and both constraints set a lower bound.",
302+
"""
303+
LowerBoundAlreadySet{
304+
$S1,
305+
$S2,
306+
}
307+
308+
You cannot add a `VariableIndex`-in-`$(S2)` constraint for variable \
309+
$(err.vi) because a `VariableIndex`-in-`$(S1)` constraint was already \
310+
set for this variable and both constraints define a lower bound.
311+
""",
296312
)
297313
end
298314

@@ -311,10 +327,16 @@ end
311327
function Base.showerror(io::IO, err::UpperBoundAlreadySet{S1,S2}) where {S1,S2}
312328
return print(
313329
io,
314-
typeof(err),
315-
": Cannot add `VariableIndex`-in-`$(S2)` constraint for variable ",
316-
"$(err.vi) as a `VariableIndex`-in-`$(S1)` constraint was already ",
317-
"set for this variable and both constraints set an upper bound.",
330+
"""
331+
UpperBoundAlreadySet{
332+
$S1,
333+
$S2,
334+
}
335+
336+
You cannot add a `VariableIndex`-in-`$(S2)` constraint for variable \
337+
$(err.vi) because a `VariableIndex`-in-`$(S1)` constraint was already \
338+
set for this variable and both constraints define an upper bound.
339+
""",
318340
)
319341
end
320342

src/indextypes.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,12 @@ end
9494
function Base.showerror(io::IO, err::InvalidIndex)
9595
return print(
9696
io,
97-
"The index $(err.index) is invalid. Note that an index becomes invalid after it has been deleted.",
97+
"""
98+
The index $(err.index) is invalid.
99+
100+
An index becomes invalid after it has been deleted. Alternatively, this \
101+
error might mean that you have re-used the index from a different model.
102+
""",
98103
)
99104
end
100105

test/Bridges/General/test_lazy_bridge_optimizer.jl

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1772,17 +1772,18 @@ function test_UnsupportedConstraint_when_it_cannot_be_bridged()
17721772
mock = MOI.Utilities.MockOptimizer(NoRSOCModel{Float64}())
17731773
bridged_mock = MOI.Bridges.LazyBridgeOptimizer(mock)
17741774
x = MOI.add_variables(bridged_mock, 4)
1775-
err = MOI.UnsupportedConstraint{
1776-
MOI.VectorOfVariables,
1777-
MOI.RotatedSecondOrderCone,
1778-
}()
1779-
@test_throws err begin
1775+
@test_throws(
1776+
MOI.UnsupportedConstraint{
1777+
MOI.VectorOfVariables,
1778+
MOI.RotatedSecondOrderCone,
1779+
},
17801780
MOI.add_constraint(
17811781
bridged_mock,
17821782
MOI.VectorOfVariables(x),
17831783
MOI.RotatedSecondOrderCone(4),
1784-
)
1785-
end
1784+
),
1785+
)
1786+
return
17861787
end
17871788

17881789
function test_MOI_runtests_No_RSOCModel()
@@ -1839,17 +1840,17 @@ function test_bridge_selection()
18391840
MOI.LogDetConeTriangle,
18401841
))
18411842
x = MOI.add_variables(bridged_mock, 3)
1842-
err = MOI.UnsupportedConstraint{
1843-
MOI.VectorAffineFunction{Float64},
1844-
MOI.LogDetConeTriangle,
1845-
}()
1846-
@test_throws err begin
1843+
@test_throws(
1844+
MOI.UnsupportedConstraint{
1845+
MOI.VectorAffineFunction{Float64},
1846+
MOI.LogDetConeTriangle,
1847+
},
18471848
MOI.Bridges.bridge_type(
18481849
bridged_mock,
18491850
MOI.VectorAffineFunction{Float64},
18501851
MOI.LogDetConeTriangle,
1851-
)
1852-
end
1852+
),
1853+
)
18531854
c = MOI.add_constraint(
18541855
bridged_mock,
18551856
MOI.VectorOfVariables(x),
@@ -2703,6 +2704,47 @@ function test_custom_cost_model_bridge_selection()
27032704
return
27042705
end
27052706

2707+
struct _UnsupportedSet <: MOI.AbstractScalarSet end
2708+
2709+
function test_bridge_unsupported_constraint()
2710+
model = MOI.instantiate(
2711+
MOI.Utilities.Model{Float64};
2712+
with_bridge_type = Float64,
2713+
)
2714+
x = MOI.add_variable(model)
2715+
err = MOI.UnsupportedConstraint{MOI.VariableIndex,_UnsupportedSet}(model)
2716+
@test_throws err MOI.add_constraint(model, x, _UnsupportedSet())
2717+
@test_throws err MOI.add_constrained_variable(model, _UnsupportedSet())
2718+
ret = """
2719+
UnsupportedConstraint{
2720+
MathOptInterface.VariableIndex,
2721+
$_UnsupportedSet,
2722+
}
2723+
2724+
This constraint type is not supported by the solver.
2725+
2726+
To fix this error you must choose a different solver.
2727+
2728+
## More information
2729+
2730+
Bridges are enabled, but there are none that can rewrite the constraint into a form supported by the solver.
2731+
2732+
The model that we were unable to add the constraint to is:
2733+
2734+
MOIB.LazyBridgeOptimizer{MOIU.Model{Float64}}
2735+
├ Variable bridges: none
2736+
├ Constraint bridges: none
2737+
├ Objective bridges: none
2738+
└ model: MOIU.Model{Float64}
2739+
├ ObjectiveSense: FEASIBILITY_SENSE
2740+
├ ObjectiveFunctionType: MOI.ScalarAffineFunction{Float64}
2741+
├ NumberOfVariables: 1
2742+
└ NumberOfConstraints: 0
2743+
"""
2744+
@test sprint(showerror, err) == ret
2745+
return
2746+
end
2747+
27062748
end # module
27072749

27082750
TestBridgesLazyBridgeOptimizer.runtests()

test/General/test_attributes.jl

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -392,8 +392,15 @@ end
392392

393393
function test_showerror_OptimizeInProgress()
394394
err = MOI.OptimizeInProgress(MOI.VariablePrimal())
395-
@test sprint(showerror, err) ==
396-
"$(typeof(err)): Cannot get result as the `MOI.optimize!` has not finished."
395+
ret = """
396+
Unable to get the attribute MathOptInterface.VariablePrimal(1) \
397+
because `MOI.optimize!` in progress.
398+
399+
This error occurs when you try to query an unsupported attribute from \
400+
inside a callback. Consult the solver's documentation to learn which \
401+
attributes may be queried inside a callback.
402+
"""
403+
@test sprint(showerror, err) == ret
397404
return
398405
end
399406

test/General/test_constraints.jl

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,17 @@ function test_LowerBoundAlreadySet_error()
4646
S2 = MOI.Interval{Int}
4747
err = MOI.LowerBoundAlreadySet{S1,S2}(x)
4848
@test err isa Exception
49-
@test sprint(showerror, err) ==
50-
"$(typeof(err)): Cannot add `VariableIndex`-in-`$(S2)` constraint " *
51-
"for variable $(x) as a `VariableIndex`-in-`$(S1)` constraint was " *
52-
"already set for this variable and both constraints set a lower bound."
49+
ret = """
50+
LowerBoundAlreadySet{
51+
$S1,
52+
$S2,
53+
}
54+
55+
You cannot add a `VariableIndex`-in-`$(S2)` constraint for variable \
56+
$(x) because a `VariableIndex`-in-`$(S1)` constraint was already set \
57+
for this variable and both constraints define a lower bound.
58+
"""
59+
@test sprint(showerror, err) == ret
5360
return
5461
end
5562

@@ -59,10 +66,17 @@ function test_UpperBoundAlreadySet_error()
5966
S2 = MOI.Interval{Int}
6067
err = MOI.UpperBoundAlreadySet{S1,S2}(x)
6168
@test err isa Exception
62-
@test sprint(showerror, err) ==
63-
"$(typeof(err)): Cannot add `VariableIndex`-in-`$(S2)` constraint " *
64-
"for variable $(x) as a `VariableIndex`-in-`$(S1)` constraint was " *
65-
"already set for this variable and both constraints set an upper bound."
69+
ret = """
70+
UpperBoundAlreadySet{
71+
$S1,
72+
$S2,
73+
}
74+
75+
You cannot add a `VariableIndex`-in-`$(S2)` constraint for variable \
76+
$(x) because a `VariableIndex`-in-`$(S1)` constraint was already set \
77+
for this variable and both constraints define an upper bound.
78+
"""
79+
@test sprint(showerror, err) == ret
6680
return
6781
end
6882

0 commit comments

Comments
 (0)