Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions src/Bridges/lazy_bridge_optimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,22 @@ function is_variable_bridged(b::LazyBridgeOptimizer, S::Type{<:MOI.AbstractSet})
return is_bridged(b, S) && is_variable_edge_best(b.graph, node(b, S))
end

function MOI.UnsupportedConstraint{F,S}(
model::LazyBridgeOptimizer,
) where {F<:MOI.AbstractFunction,S<:MOI.AbstractSet}
msg = """
## More information

Bridges are enabled, but there are none that can rewrite the \
constraint into a form supported by the solver.

The model that we were unable to add the constraint to is:

$(sprint(show, model))
Comment on lines +554 to +558

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
constraint into a form supported by the solver.
The model that we were unable to add the constraint to is:
$(sprint(show, model))
constraint into a form supported by the solver $(sprint(show, MOI.get(model, MOI.SolverName())))

I'd just print the solver name instead of the whole model, since the solver is what makes the constraint unsupported.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this isn't print(model), but it's show(model). Which is the high level summary.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which still has a lot of irrelevant information.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SolverName drops a lot of relevant information.

julia> MOI.get(b, MOI.SolverName())
"Dual model with Hypatia attached"

It looks through the bridging layer and the cache.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is the result, it's exactly the information I wanted to have, which is why I suggested it.

It's just a suggestion, feel free to ignore it.

"""
return MOI.UnsupportedConstraint{F,S}(msg)
end

function bridge_type(b::LazyBridgeOptimizer, S::Type{<:MOI.AbstractSet})
bt = get(b.cached_bridge_type, (S,), nothing)
if bt !== nothing
Expand All @@ -552,7 +568,7 @@ function bridge_type(b::LazyBridgeOptimizer, S::Type{<:MOI.AbstractSet})
index = bridge_index(b, S)
if iszero(index)
F = MOI.Utilities.variable_function_type(S)
throw(MOI.UnsupportedConstraint{F,S}())
throw(MOI.UnsupportedConstraint{F,S}(b))
end
new_bt = Variable.concrete_bridge_type(b.variable_bridge_types[index], S)
b.cached_bridge_type[(S,)] = new_bt
Expand All @@ -570,7 +586,7 @@ function bridge_type(
end
index = bridge_index(b, F, S)
if iszero(index)
throw(MOI.UnsupportedConstraint{F,S}())
throw(MOI.UnsupportedConstraint{F,S}(b))
end
new_bt =
Constraint.concrete_bridge_type(b.constraint_bridge_types[index], F, S)
Expand Down
48 changes: 36 additions & 12 deletions src/attributes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,11 @@ end
function Base.showerror(io::IO, err::ResultIndexBoundsError)
return print(
io,
"Result index of attribute $(err.attr) out of bounds. There are " *
"currently $(err.result_count) solution(s) in the model.",
"""
Result index of attribute $(err.attr) is out of bounds.

There are currently $(err.result_count) solution(s) in the model.
""",
)
end

Expand Down Expand Up @@ -285,8 +288,11 @@ end
function Base.showerror(io::IO, err::ConflictIndexBoundsError)
return print(
io,
"Conflict index of attribute $(err.attr) out of bounds. There are " *
"currently $(err.conflict_count) conflict(s) in the model.",
"""
Conflict index of attribute $(err.attr) is out of bounds.

There are currently $(err.conflict_count) conflict(s) in the model.
""",
)
end

Expand Down Expand Up @@ -1261,9 +1267,13 @@ end
function Base.showerror(io::IO, err::OptimizeInProgress)
return print(
io,
typeof(err),
": Cannot get result as the `MOI.optimize!` has not",
" finished.",
"""
Unable to get the attribute $(err.attr) because `MOI.optimize!` in progress.

This error occurs when you try to query an unsupported attribute from \
inside a callback. Consult the solver's documentation to learn which \
attributes may be queried inside a callback.
""",
)
end

Expand Down Expand Up @@ -2633,8 +2643,15 @@ struct FunctionTypeMismatch{F1,F2} <: Exception end
function Base.showerror(io::IO, err::FunctionTypeMismatch{F1,F2}) where {F1,F2}
return print(
io,
"""$(typeof(err)): Cannot modify functions of different types.
Constraint type is $F1 while the replacement function is of type $F2.""",
"""
Cannot modify functions of different types.

The original function is a $F1

The replacement function is a $F2

When modifying the `ConstraintFunction` the types must be the same.
""",
)
end

Expand Down Expand Up @@ -2714,9 +2731,16 @@ struct SetTypeMismatch{S1,S2} <: Exception end
function Base.showerror(io::IO, err::SetTypeMismatch{S1,S2}) where {S1,S2}
return print(
io,
"""$(typeof(err)): Cannot modify sets of different types. Constraint
type is $S1 while the replacement set is of type $S2. Use `transform`
instead.""",
"""
Cannot modify sets of different types.

The original set is a $S1

The replacement set is a $S2

When modifying the `ConstraintSet` the types must be the same.
Alternatively, you may have meant to call `MOI.transform` instead.
""",
)
end

Expand Down
60 changes: 41 additions & 19 deletions src/constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,35 +35,45 @@ the model, that is, that [`supports_constraint`](@ref) returns `false`.

```jldoctest
julia> showerror(stdout, MOI.UnsupportedConstraint{MOI.VariableIndex,MOI.ZeroOne}())
UnsupportedConstraint: `MathOptInterface.VariableIndex`-in-`MathOptInterface.ZeroOne` constraints are not supported by the
solver you have chosen, and we could not reformulate your model into a
form that is supported.
UnsupportedConstraint{
MathOptInterface.VariableIndex,
MathOptInterface.ZeroOne,
}

To fix this error you must choose a different solver.
This constraint type is not supported by the solver.

To fix this error you must choose a different solver.
```
"""
struct UnsupportedConstraint{F<:AbstractFunction,S<:AbstractSet} <:
UnsupportedError
# Human-friendly explanation why the attribute cannot be set
message::String
end

UnsupportedConstraint{F,S}() where {F,S} = UnsupportedConstraint{F,S}("")
function UnsupportedConstraint{F,S}(
msg::String = "",
) where {F<:AbstractFunction,S<:AbstractSet}
return new{F,S}(msg)
end
end

function Base.showerror(io::IO, err::UnsupportedConstraint{F,S}) where {F,S}
print(
io,
"""
UnsupportedConstraint: `$F`-in-`$S` constraints are not supported by the
solver you have chosen, and we could not reformulate your model into a
form that is supported.
UnsupportedConstraint{
$F,
$S,
}

To fix this error you must choose a different solver.
This constraint type is not supported by the solver.

$(err.message)
To fix this error you must choose a different solver.
""",
)
if !isempty(err.message)
print(io, "\n", err.message)
end
return
end

Expand Down Expand Up @@ -289,10 +299,16 @@ end
function Base.showerror(io::IO, err::LowerBoundAlreadySet{S1,S2}) where {S1,S2}
return print(
io,
typeof(err),
": Cannot add `VariableIndex`-in-`$(S2)` constraint for variable ",
"$(err.vi) as a `VariableIndex`-in-`$(S1)` constraint was already ",
"set for this variable and both constraints set a lower bound.",
"""
LowerBoundAlreadySet{
$S1,
$S2,
}

You cannot add a `VariableIndex`-in-`$(S2)` constraint for variable \
$(err.vi) because a `VariableIndex`-in-`$(S1)` constraint was already \
set for this variable and both constraints define a lower bound.
""",
)
end

Expand All @@ -311,10 +327,16 @@ end
function Base.showerror(io::IO, err::UpperBoundAlreadySet{S1,S2}) where {S1,S2}
return print(
io,
typeof(err),
": Cannot add `VariableIndex`-in-`$(S2)` constraint for variable ",
"$(err.vi) as a `VariableIndex`-in-`$(S1)` constraint was already ",
"set for this variable and both constraints set an upper bound.",
"""
UpperBoundAlreadySet{
$S1,
$S2,
}

You cannot add a `VariableIndex`-in-`$(S2)` constraint for variable \
$(err.vi) because a `VariableIndex`-in-`$(S1)` constraint was already \
set for this variable and both constraints define an upper bound.
""",
)
end

Expand Down
7 changes: 6 additions & 1 deletion src/indextypes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,12 @@ end
function Base.showerror(io::IO, err::InvalidIndex)
return print(
io,
"The index $(err.index) is invalid. Note that an index becomes invalid after it has been deleted.",
"""
The index $(err.index) is invalid.

An index becomes invalid after it has been deleted. Alternatively, this \
error might mean that you have re-used the index from a different model.
""",
)
end

Expand Down
70 changes: 56 additions & 14 deletions test/Bridges/General/test_lazy_bridge_optimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1772,17 +1772,18 @@ function test_UnsupportedConstraint_when_it_cannot_be_bridged()
mock = MOI.Utilities.MockOptimizer(NoRSOCModel{Float64}())
bridged_mock = MOI.Bridges.LazyBridgeOptimizer(mock)
x = MOI.add_variables(bridged_mock, 4)
err = MOI.UnsupportedConstraint{
MOI.VectorOfVariables,
MOI.RotatedSecondOrderCone,
}()
@test_throws err begin
@test_throws(
MOI.UnsupportedConstraint{
MOI.VectorOfVariables,
MOI.RotatedSecondOrderCone,
},
MOI.add_constraint(
bridged_mock,
MOI.VectorOfVariables(x),
MOI.RotatedSecondOrderCone(4),
)
end
),
)
return
end

function test_MOI_runtests_No_RSOCModel()
Expand Down Expand Up @@ -1839,17 +1840,17 @@ function test_bridge_selection()
MOI.LogDetConeTriangle,
))
x = MOI.add_variables(bridged_mock, 3)
err = MOI.UnsupportedConstraint{
MOI.VectorAffineFunction{Float64},
MOI.LogDetConeTriangle,
}()
@test_throws err begin
@test_throws(
MOI.UnsupportedConstraint{
MOI.VectorAffineFunction{Float64},
MOI.LogDetConeTriangle,
},
MOI.Bridges.bridge_type(
bridged_mock,
MOI.VectorAffineFunction{Float64},
MOI.LogDetConeTriangle,
)
end
),
)
c = MOI.add_constraint(
bridged_mock,
MOI.VectorOfVariables(x),
Expand Down Expand Up @@ -2703,6 +2704,47 @@ function test_custom_cost_model_bridge_selection()
return
end

struct _UnsupportedSet <: MOI.AbstractScalarSet end

function test_bridge_unsupported_constraint()
model = MOI.instantiate(
MOI.Utilities.Model{Float64};
with_bridge_type = Float64,
)
x = MOI.add_variable(model)
err = MOI.UnsupportedConstraint{MOI.VariableIndex,_UnsupportedSet}(model)
@test_throws err MOI.add_constraint(model, x, _UnsupportedSet())
@test_throws err MOI.add_constrained_variable(model, _UnsupportedSet())
ret = """
UnsupportedConstraint{
MathOptInterface.VariableIndex,
$_UnsupportedSet,
}

This constraint type is not supported by the solver.

To fix this error you must choose a different solver.

## More information

Bridges are enabled, but there are none that can rewrite the constraint into a form supported by the solver.

The model that we were unable to add the constraint to is:

MOIB.LazyBridgeOptimizer{MOIU.Model{Float64}}
├ Variable bridges: none
├ Constraint bridges: none
├ Objective bridges: none
└ model: MOIU.Model{Float64}
├ ObjectiveSense: FEASIBILITY_SENSE
├ ObjectiveFunctionType: MOI.ScalarAffineFunction{Float64}
├ NumberOfVariables: 1
└ NumberOfConstraints: 0
"""

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@araujoms here you go

@test sprint(showerror, err) == ret
return
end

end # module

TestBridgesLazyBridgeOptimizer.runtests()
11 changes: 9 additions & 2 deletions test/General/test_attributes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -392,8 +392,15 @@ end

function test_showerror_OptimizeInProgress()
err = MOI.OptimizeInProgress(MOI.VariablePrimal())
@test sprint(showerror, err) ==
"$(typeof(err)): Cannot get result as the `MOI.optimize!` has not finished."
ret = """
Unable to get the attribute MathOptInterface.VariablePrimal(1) \
because `MOI.optimize!` in progress.

This error occurs when you try to query an unsupported attribute from \
inside a callback. Consult the solver's documentation to learn which \
attributes may be queried inside a callback.
"""
@test sprint(showerror, err) == ret
return
end

Expand Down
30 changes: 22 additions & 8 deletions test/General/test_constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,17 @@ function test_LowerBoundAlreadySet_error()
S2 = MOI.Interval{Int}
err = MOI.LowerBoundAlreadySet{S1,S2}(x)
@test err isa Exception
@test sprint(showerror, err) ==
"$(typeof(err)): Cannot add `VariableIndex`-in-`$(S2)` constraint " *
"for variable $(x) as a `VariableIndex`-in-`$(S1)` constraint was " *
"already set for this variable and both constraints set a lower bound."
ret = """
LowerBoundAlreadySet{
$S1,
$S2,
}

You cannot add a `VariableIndex`-in-`$(S2)` constraint for variable \
$(x) because a `VariableIndex`-in-`$(S1)` constraint was already set \
for this variable and both constraints define a lower bound.
"""
@test sprint(showerror, err) == ret
return
end

Expand All @@ -59,10 +66,17 @@ function test_UpperBoundAlreadySet_error()
S2 = MOI.Interval{Int}
err = MOI.UpperBoundAlreadySet{S1,S2}(x)
@test err isa Exception
@test sprint(showerror, err) ==
"$(typeof(err)): Cannot add `VariableIndex`-in-`$(S2)` constraint " *
"for variable $(x) as a `VariableIndex`-in-`$(S1)` constraint was " *
"already set for this variable and both constraints set an upper bound."
ret = """
UpperBoundAlreadySet{
$S1,
$S2,
}

You cannot add a `VariableIndex`-in-`$(S2)` constraint for variable \
$(x) because a `VariableIndex`-in-`$(S1)` constraint was already set \
for this variable and both constraints define an upper bound.
"""
@test sprint(showerror, err) == ret
return
end

Expand Down
Loading
Loading