Skip to content

Commit

Permalink
Merge pull request #1713 from JuliaOpt/bl/jumpextprint
Browse files Browse the repository at this point in the history
✅ Fix JuMPExtension print tests
  • Loading branch information
blegat committed Jan 6, 2019
2 parents 3657606 + c8d6ef4 commit 8e810c5
Show file tree
Hide file tree
Showing 4 changed files with 248 additions and 68 deletions.
14 changes: 14 additions & 0 deletions src/nlp.jl
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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)
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
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
Original file line number Diff line number Diff line change
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
Loading

0 comments on commit 8e810c5

Please sign in to comment.