Skip to content

Commit

Permalink
Merge pull request jump-dev#1646 from JuliaOpt/bl/print
Browse files Browse the repository at this point in the history
Implement model printing
  • Loading branch information
blegat authored Dec 6, 2018
2 parents a4523ab + 2fc482e commit 567097a
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 121 deletions.
5 changes: 5 additions & 0 deletions docs/src/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ used to specify the optimizer to be used:
```julia
julia> model = Model(with_optimizer(GLPK.Optimizer))
A JuMP Model
Feasibility problem with:
Variables: 0
Model mode: Automatic
CachingOptimizer state: NoOptimizer
Solver name: No optimizer attached.
```

```@meta
Expand Down
1 change: 1 addition & 0 deletions docs/src/variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ To illustrate these three types of variables, consider the following JuMP code
```jldoctest variables
julia> model = Model()
A JuMP Model
Feasibility problem with:
Variables: 0
Model mode: Automatic
CachingOptimizer state: NoOptimizer
Expand Down
1 change: 1 addition & 0 deletions src/macros.jl
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,7 @@ To minimize the value of the variable `x`, do as follows:
```jldoctest @objective; setup = :(using JuMP)
julia> model = Model()
A JuMP Model
Feasibility problem with:
Variables: 0
Model mode: Automatic
CachingOptimizer state: NoOptimizer
Expand Down
1 change: 1 addition & 0 deletions src/objective.jl
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ Error if the objective is not convertible to type `T`.
```jldoctest objective_function; setup = :(using JuMP)
julia> model = Model()
A JuMP Model
Feasibility problem with:
Variables: 0
Model mode: Automatic
CachingOptimizer state: NoOptimizer
Expand Down
91 changes: 77 additions & 14 deletions src/print.jl
Original file line number Diff line number Diff line change
Expand Up @@ -147,33 +147,91 @@ wrap_in_inline_math_mode(str) = "\$ $str \$"
## Model
#------------------------------------------------------------------------
function Base.show(io::IO, model::Model)
plural(n) = (n==1 ? "" : "s")
println(io, "A JuMP Model")
sense = objective_sense(model)
if sense == MOI.MaxSense
print(io, "Maximization")
elseif sense == MOI.MinSense
print(io, "Minimization")
else
print(io, "Feasibility")
end
println(io, " problem with:")
# TODO: Consider allowing a JuMP model to have a string name.
println(io, "Variables: ", num_variables(model))
println(io, "Variable", plural(num_variables(model)), ": ",
num_variables(model))
# https://github.com/JuliaOpt/JuMP.jl/issues/1556
# TODO: This doesn't account for nonlinear objectives
# println(io, "\tObjective function type:",
# MOI.get(model, MOI.ObjectiveFunctionType()))
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 constraints")
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), " constraints")
println(io, "Nonlinear: ", num_nl_constraints(model), " constraint",
plural(num_nl_constraints(model)))
end
model_mode = mode(model)
println(io, "Model mode: ", model_mode)
if model_mode == Manual || model_mode == Automatic
println(io, "CachingOptimizer state: ",
MOIU.state(backend(model)))
end
println(io, "Solver name: ", solver_name(model))
names_in_scope = collect(keys(object_dictionary(model)))
# 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, "Names registered in the model: ",
join(string.(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)
print(io, model_string(REPLMode, model))
end
function Base.show(io::IO, ::MIME"text/latex", model::Model)
print(io, wrap_in_math_mode(model_string(IJuliaMode, model)))
end
function model_string(print_mode, model::Model)
ijl = print_mode == IJuliaMode
sep = ijl ? " & " : " "
eol = ijl ? "\\\\\n" : "\n"
sense = objective_sense(model)
str = ""
if sense == MOI.MaxSense
str *= ijl ? "\\max" : "Max"
elseif sense == MOI.MinSense
str *= ijl ? "\\min" : "Min"
else
str *= ijl ? "\\text{feasibility}" : "Feasibility"
end
# TODO: The last print shouldn't have a new line
if sense != MOI.FeasibilitySense
if ijl
str *= "\\quad"
end
str *= sep
str *= function_string(print_mode,
objective_function(model, QuadExpr))
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 ijl
str = "\\begin{alignat*}{1}" * str * "\\end{alignat*}\n"
end
return str
end

#------------------------------------------------------------------------
Expand Down Expand Up @@ -307,10 +365,10 @@ end
# `JuMP.jump_function` or `JuMP.function_string` and either `JuMP.moi_set` or
# `JuMP.in_set_string` should be implemented.
function Base.show(io::IO, ref::ConstraintRef)
print(io, constraint_string(REPLMode, name(ref), constraint_object(ref)))
print(io, constraint_string(REPLMode, ref))
end
function Base.show(io::IO, ::MIME"text/latex", ref::ConstraintRef)
print(io, constraint_string(IJuliaMode, name(ref), constraint_object(ref)))
print(io, constraint_string(IJuliaMode, ref))
end

"""
Expand Down Expand Up @@ -403,12 +461,14 @@ function in_set_string(print_mode, constraint::AbstractConstraint)
return in_set_string(print_mode, moi_set(constraint))
end

# constraint_object is a JuMP constraint object like AffExprConstraint.
# Assumes a .func and .set member.
function constraint_string(print_mode, constraint_name, constraint_object)
function constraint_string(print_mode, constraint_object::AbstractConstraint)
func_str = function_string(print_mode, constraint_object)
in_set_str = in_set_string(print_mode, constraint_object)
constraint_without_name = func_str * " " * in_set_str
return func_str * " " * in_set_str
end
function constraint_string(print_mode, constraint_name,
constraint_object::AbstractConstraint)
constraint_without_name = constraint_string(print_mode, constraint_object)
if print_mode == IJuliaMode
constraint_without_name = wrap_in_inline_math_mode(constraint_without_name)
end
Expand All @@ -418,6 +478,9 @@ function constraint_string(print_mode, constraint_name, constraint_object)
return constraint_name * " : " * constraint_without_name
end
end
function constraint_string(print_mode, ref::ConstraintRef)
return constraint_string(print_mode, name(ref), constraint_object(ref))
end

#------------------------------------------------------------------------
## NonlinearExprData
Expand Down
107 changes: 0 additions & 107 deletions test/old/print.jl
Original file line number Diff line number Diff line change
Expand Up @@ -336,113 +336,6 @@ end
end



@testset "Model" begin
le, ge, eq, fa = repl[:leq], repl[:geq], repl[:eq], repl[:for_all]
inset, dots = repl[:in], repl[:dots]
infty, union = repl[:infty], repl[:union]
Vert, sub2 = repl[:Vert], repl[:sub2]

#------------------------------------------------------------------

mod_1 = Model()
@variable(mod_1, a>=1)
@variable(mod_1, b<=1)
@variable(mod_1, -1<=c<=1)
@variable(mod_1, a1>=1,Int)
@variable(mod_1, b1<=1,Int)
@variable(mod_1, -1<=c1<=1,Int)
@variable(mod_1, x, Bin)
@variable(mod_1, y)
@variable(mod_1, z, Int)
@variable(mod_1, sos[1:3], Bin)
@variable(mod_1, 2 <= si <= 3, SemiInt)
@variable(mod_1, 2 <= sc <= 3, SemiCont)
@variable(mod_1, fi == 9)
@objective(mod_1, Max, a - b + 2a1 - 10x)
@constraint(mod_1, a + b - 10c - 2x + c1 <= 1)
@constraint(mod_1, a*b <= 2)
addSOS1(mod_1, [i*sos[i] for i in 1:3])
@constraint(mod_1, norm(sos) + a <= 1)

io_test(REPLMode, mod_1, """
Max a - b + 2 a1 - 10 x
Subject to
a + b - 10 c - 2 x + c1 $le 1
a*b - 2 $le 0
SOS1: {1 sos[1], 2 sos[2], 3 sos[3]}
$(Vert)[sos[1],sos[2],sos[3]]$(Vert)$(sub2) $le -a + 1
sos[i] $inset {0,1} $fa i $inset {1,2,3}
a $ge 1
b $le 1
-1 $le c $le 1
a1 $ge 1, integer
b1 $le 1, integer
-1 $le c1 $le 1, integer
x $inset {0,1}
y
z, integer
si $inset {2,$dots,3} $union {0}
sc $inset [2,3] $union {0}
fi = 9
""", repl=:print)

io_test(REPLMode, mod_1, """
Maximization problem with:
* 1 linear constraint
* 1 quadratic constraint
* 1 SOS constraint
* 1 SOC constraint
* 15 variables: 4 binary, 4 integer, 1 semicontinuous, 1 semi-integer
Solver is default solver""", repl=:show)

io_test(IJuliaMode, mod_1, """
\\begin{alignat*}{1}\\max\\quad & a - b + 2 a1 - 10 x\\\\
\\text{Subject to} \\quad & a + b - 10 c - 2 x + c1 \\leq 1\\\\
& a\\times b - 2 \\leq 0\\\\
& SOS1: \\{1 sos[1], 2 sos[2], 3 sos[3]\\}\\\\
& \\Vert[sos_{1},sos_{2},sos_{3}]\\Vert_2 $le -a + 1\\\\
& sos_{i} \\in \\{0,1\\} \\quad\\forall i \\in \\{1,2,3\\}\\\\
& a \\geq 1\\\\
& b \\leq 1\\\\
& -1 \\leq c \\leq 1\\\\
& a1 \\geq 1, \\in \\mathbb{Z}\\\\
& b1 \\leq 1, \\in \\mathbb{Z}\\\\
& -1 \\leq c1 \\leq 1, \\in \\mathbb{Z}\\\\
& x \\in \\{0,1\\}\\\\
& y\\\\
& z, \\in \\mathbb{Z}\\\\
& si \\in \\{2,\\dots,3\\} \\cup \\{0\\}\\\\
& sc \\in \\[2,3\\] \\cup \\{0\\}\\\\
& fi = 9\\\\
\\end{alignat*}
""")

#------------------------------------------------------------------

mod_2 = Model()
@variable(mod_2, x, Bin)
@variable(mod_2, y, Int)
@constraint(mod_2, x*y <= 1)

io_test(REPLMode, mod_2, """
Feasibility problem with:
* 0 linear constraints
* 1 quadratic constraint
* 2 variables: 1 binary, 1 integer
Solver is default solver""", repl=:show)

mod_2 = Model()
@variable(mod_2, x)
@constraint(mod_2, x <= 3)

io_test(REPLMode, mod_2, """
Feasibility problem with:
* 1 linear constraint
* 1 variable
Solver is default solver""", repl=:show)
end

@testset "changing variable categories" begin
le, ge, fa = repl[:leq], repl[:geq], repl[:for_all]
inset, dots = repl[:in], repl[:dots]
Expand Down
Loading

0 comments on commit 567097a

Please sign in to comment.