Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✅ Fix JuMPExtension print tests #1713

Merged
merged 11 commits into from Jan 6, 2019
14 changes: 14 additions & 0 deletions src/nlp.jl
Expand Up @@ -71,6 +71,20 @@ mutable struct NLPData
evaluator
end

"""
nlp_objective_function(model::Model)

Returns the nonlinear objective function or `nothing` if no nonlinear objective
function is set.
"""
function nlp_objective_function(model::Model)
if model.nlp_data === nothing
return nothing
else
return model.nlp_data.nlobj
end
end

function create_nlp_block_data(m::Model)
@assert m.nlp_data !== nothing
bounds = MOI.NLPBoundsPair[]
Expand Down
143 changes: 97 additions & 46 deletions src/print.jl
Expand Up @@ -143,11 +143,15 @@ end
wrap_in_math_mode(str) = "\$\$ $str \$\$"
wrap_in_inline_math_mode(str) = "\$ $str \$"

plural(n) = (n==1 ? "" : "s")

#------------------------------------------------------------------------
## Model
#------------------------------------------------------------------------
function Base.show(io::IO, model::Model)
plural(n) = (n==1 ? "" : "s")

# An `AbstractModel` subtype should implement `show_objective_function_summary`,
# `show_constraints_summary` and `show_backend_summary` for this method to work.
function Base.show(io::IO, model::AbstractModel)
println(io, "A JuMP Model")
sense = objective_sense(model)
if sense == MOI.MAX_SENSE
Expand All @@ -162,22 +166,19 @@ function Base.show(io::IO, model::Model)
println(io, "Variable", plural(num_variables(model)), ": ",
num_variables(model))
if sense != MOI.FEASIBILITY_SENSE
if model.nlp_data !== nothing && model.nlp_data.nlobj !== nothing
println(io, "Objective function type: Nonlinear")
else
println(io, "Objective function type: ",
MOI.get(model, MOI.ObjectiveFunctionType()))
end
end
for (F, S) in MOI.get(model, MOI.ListOfConstraints())
num_constraints = MOI.get(model, MOI.NumberOfConstraints{F, S}())
println(io, "`$F`-in-`$S`: $num_constraints constraint",
plural(num_constraints))
show_objective_function_summary(io, model)
end
if !iszero(num_nl_constraints(model))
println(io, "Nonlinear: ", num_nl_constraints(model), " constraint",
plural(num_nl_constraints(model)))
show_constraints_summary(io, model)
show_backend_summary(io, model)
names_in_scope = sort(collect(keys(object_dictionary(model))))
if !isempty(names_in_scope)
println(io)
print(io, "Names registered in the model: ",
join(string.(names_in_scope), ", "))
end
end

function show_backend_summary(io::IO, model::Model)
model_mode = mode(model)
println(io, "Model mode: ", model_mode)
if model_mode == MANUAL || model_mode == AUTOMATIC
Expand All @@ -186,21 +187,18 @@ function Base.show(io::IO, model::Model)
end
# The last print shouldn't have a new line
print(io, "Solver name: ", solver_name(model))
names_in_scope = sort(collect(keys(object_dictionary(model))))
if !isempty(names_in_scope)
println(io)
print(io, "Names registered in the model: ",
join(string.(names_in_scope), ", "))
end
end

function Base.print(io::IO, model::Model)
function Base.print(io::IO, model::AbstractModel)
print(io, model_string(REPLMode, model))
end
function Base.show(io::IO, ::MIME"text/latex", model::Model)
function Base.show(io::IO, ::MIME"text/latex", model::AbstractModel)
print(io, wrap_in_math_mode(model_string(IJuliaMode, model)))
end
function model_string(print_mode, model::Model)

# An `AbstractModel` subtype should implement `objective_function_string` and
# `constraints_string` for this method to work.
function model_string(print_mode, model::AbstractModel)
ijl = print_mode == IJuliaMode
sep = ijl ? " & " : " "
eol = ijl ? "\\\\\n" : "\n"
Expand All @@ -218,36 +216,46 @@ function model_string(print_mode, model::Model)
str *= "\\quad"
end
str *= sep
if model.nlp_data !== nothing && model.nlp_data.nlobj !== nothing
str *= nl_expr_string(model, print_mode, model.nlp_data.nlobj)
else
str *= function_string(print_mode,
objective_function(model, QuadExpr))
end
str *= objective_function_string(print_mode, model)
end
str *= eol
str *= ijl ? "\\text{Subject to} \\quad" : "Subject to" * eol
for (F, S) in MOI.get(model, MOI.ListOfConstraints())
for idx in MOI.get(model, MOI.ListOfConstraintIndices{F, S}())
# FIXME the shape may be incorrect here
shape = S <: MOI.AbstractScalarSet ? ScalarShape() : VectorShape()
cref = ConstraintRef(model, idx, shape)
con = constraint_object(cref)
str *= sep * constraint_string(print_mode, con) * eol
end
end
if model.nlp_data !== nothing
for nl_constraint in model.nlp_data.nlconstr
str *= sep * nl_constraint_string(model, print_mode, nl_constraint)
str *= eol
end
end
str *= constraints_string(print_mode, model, sep, eol)
if ijl
str = "\\begin{alignat*}{1}" * str * "\\end{alignat*}\n"
end
return str
end

"""
show_objective_function_summary(io::IO, model::AbstractModel)

Write to `io` a summary of the objective function type.
"""
function show_objective_function_summary(io::IO, model::Model)
nlobj = nlp_objective_function(model)
print(io, "Objective function type: ")
if nlobj === nothing
println(io, objective_function_type(model))
else
println(io, "Nonlinear")
end
end

"""
objective_function_string(print_mode, model::AbstractModel)::String

Return a `String` describing the objective function of the model.
"""
function objective_function_string(print_mode, model::Model)
nlobj = nlp_objective_function(model)
if nlobj === nothing
return function_string(print_mode, objective_function(model))
else
return nl_expr_string(model, print_mode, nlobj)
end
end

#------------------------------------------------------------------------
## VariableRef
#------------------------------------------------------------------------
Expand Down Expand Up @@ -370,6 +378,49 @@ end
## Constraints
#------------------------------------------------------------------------

"""
show_constraints_summary(io::IO, model::AbstractModel)

Write to `io` a summary of the number of constraints.
"""
function show_constraints_summary(io::IO, model::Model)
blegat marked this conversation as resolved.
Show resolved Hide resolved
for (F, S) in MOI.get(model, MOI.ListOfConstraints())
num_constraints = MOI.get(model, MOI.NumberOfConstraints{F, S}())
println(io, "`$F`-in-`$S`: $num_constraints constraint",
plural(num_constraints))
end
if !iszero(num_nl_constraints(model))
println(io, "Nonlinear: ", num_nl_constraints(model), " constraint",
plural(num_nl_constraints(model)))
end
end

"""
constraints_string(print_mode, model::AbstractModel, sep, eol)::String

Return a `String` describing the constraints of the model, each on a line
starting with `sep` and ending with `eol` (which already contains `\n`).
"""
function constraints_string(print_mode, model::Model, sep, eol)
str = ""
for (F, S) in MOI.get(model, MOI.ListOfConstraints())
for idx in MOI.get(model, MOI.ListOfConstraintIndices{F, S}())
# FIXME the shape may be incorrect here
blegat marked this conversation as resolved.
Show resolved Hide resolved
shape = S <: MOI.AbstractScalarSet ? ScalarShape() : VectorShape()
cref = ConstraintRef(model, idx, shape)
con = constraint_object(cref)
str *= sep * constraint_string(print_mode, con) * eol
end
end
if model.nlp_data !== nothing
for nl_constraint in model.nlp_data.nlconstr
str *= sep * nl_constraint_string(model, print_mode, nl_constraint)
str *= eol
end
end
return str
end

## Notes for extensions
# For a `ConstraintRef{ModelType, IndexType}` where `ModelType` is not
# `JuMP.Model` or `IndexType` is not `MathOptInterface.ConstraintIndex`, the
Expand Down
23 changes: 23 additions & 0 deletions test/JuMPExtension.jl
Expand Up @@ -299,4 +299,27 @@ function JuMP.constraint_by_name(model::MyModel, name::String)
end
end

# Show
function JuMP.show_backend_summary(io::IO, model::MyModel) end
function JuMP.show_objective_function_summary(io::IO, model::MyModel)
println(io, "Objective function type: ",
JuMP.objective_function_type(model))
end
function JuMP.objective_function_string(print_mode, model::MyModel)
return JuMP.function_string(print_mode, JuMP.objective_function(model))
end
function JuMP.show_constraints_summary(io::IO, model::MyModel)
n = length(model.constraints)
print(io, "Constraint", JuMP.plural(n), ": ", n)
end
function JuMP.constraints_string(print_mode, model::MyModel, sep, eol)
str = ""
# Sort by creation order, i.e. ConstraintIndex value
constraints = sort(collect(model.constraints), by = c -> c.first.value)
for (index, constraint) in constraints
str *= sep * JuMP.constraint_string(print_mode, constraint) * eol
end
return str
end

end