Skip to content

Commit

Permalink
Implement model printing
Browse files Browse the repository at this point in the history
  • Loading branch information
blegat committed Nov 28, 2018
1 parent e5264e7 commit 2ad444d
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 120 deletions.
89 changes: 76 additions & 13 deletions src/print.jl
Original file line number Diff line number Diff line change
Expand Up @@ -147,34 +147,92 @@ 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()))
constraint_types = MOI.get(model, MOI.ListOfConstraints())
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))
# The last print shouldn't have a new line
print(io, "Solver name: ", solver_name(model))
names_in_scope = 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 @@ -308,10 +366,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 @@ -404,12 +462,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 @@ -419,6 +479,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
129 changes: 129 additions & 0 deletions test/print.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# test/print.jl
# Testing $fa pretty-printing-related functionality
#############################################################################
using MathOptInterface
using JuMP
using Compat
using Compat.Test
Expand Down Expand Up @@ -348,6 +349,134 @@ function printing_test(ModelType::Type{<:JuMP.AbstractModel})
io_test(REPLMode, quad_constr, "2 x$sq $le 1.0")
# TODO: Test in IJulia mode.
end
@testset "Model" begin
repl(s) = JuMP.math_symbol(REPLMode, s)
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)
for_all = repl(:for_all)

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

model_1 = Model()
@variable(model_1, a>=1)
@variable(model_1, b<=1)
@variable(model_1, -1<=c<=1)
@variable(model_1, a1>=1,Int)
@variable(model_1, b1<=1,Int)
@variable(model_1, -1<=c1<=1,Int)
@variable(model_1, x, Bin)
@variable(model_1, y)
@variable(model_1, z, Int)
@variable(model_1, u[1:3], Bin)
@variable(model_1, fi == 9)
@objective(model_1, Max, a - b + 2a1 - 10x)
@constraint(model_1, a + b - 10c - 2x + c1 <= 1)
@constraint(model_1, a*b <= 2)
@constraint(model_1, [1 - a; u] in SecondOrderCone())

io_test(REPLMode, model_1, """
Max a - b + 2 a1 - 10 x
Subject to
x $inset MathOptInterface.ZeroOne()
u[1] $inset MathOptInterface.ZeroOne()
u[2] $inset MathOptInterface.ZeroOne()
u[3] $inset MathOptInterface.ZeroOne()
a1 $inset MathOptInterface.Integer()
b1 $inset MathOptInterface.Integer()
c1 $inset MathOptInterface.Integer()
z $inset MathOptInterface.Integer()
fi = 9.0
a $ge 1.0
c $ge -1.0
a1 $ge 1.0
c1 $ge -1.0
b $le 1.0
c $le 1.0
b1 $le 1.0
c1 $le 1.0
a + b - 10 c - 2 x + c1 $le 1.0
a*b $le 2.0
[-a + 1, u[1], u[2], u[3]] $inset MathOptInterface.SecondOrderCone(4)
""", repl=:print)


io_test(REPLMode, model_1, """
A JuMP Model
Maximization problem with:
Variables: 13
`MathOptInterface.SingleVariable`-in-`MathOptInterface.ZeroOne`: 4 constraints
`MathOptInterface.SingleVariable`-in-`MathOptInterface.Integer`: 4 constraints
`MathOptInterface.SingleVariable`-in-`MathOptInterface.EqualTo{Float64}`: 1 constraint
`MathOptInterface.SingleVariable`-in-`MathOptInterface.GreaterThan{Float64}`: 4 constraints
`MathOptInterface.SingleVariable`-in-`MathOptInterface.LessThan{Float64}`: 4 constraints
`MathOptInterface.ScalarAffineFunction{Float64}`-in-`MathOptInterface.LessThan{Float64}`: 1 constraint
`MathOptInterface.ScalarQuadraticFunction{Float64}`-in-`MathOptInterface.LessThan{Float64}`: 1 constraint
`MathOptInterface.VectorAffineFunction{Float64}`-in-`MathOptInterface.SecondOrderCone`: 1 constraint
Model mode: Automatic
CachingOptimizer state: NoOptimizer
Solver name: No optimizer attached.
Names registered in the model: b, c, c1, b1, a1, x, fi, z, u, a, y""", repl=:show)

io_test(IJuliaMode, model_1, """
\\begin{alignat*}{1}\\max\\quad & a - b + 2 a1 - 10 x\\\\
\\text{Subject to} \\quad & x \\in MathOptInterface.ZeroOne()\\\\
& u_{1} \\in MathOptInterface.ZeroOne()\\\\
& u_{2} \\in MathOptInterface.ZeroOne()\\\\
& u_{3} \\in MathOptInterface.ZeroOne()\\\\
& a1 \\in MathOptInterface.Integer()\\\\
& b1 \\in MathOptInterface.Integer()\\\\
& c1 \\in MathOptInterface.Integer()\\\\
& z \\in MathOptInterface.Integer()\\\\
& fi = 9.0\\\\
& a \\geq 1.0\\\\
& c \\geq -1.0\\\\
& a1 \\geq 1.0\\\\
& c1 \\geq -1.0\\\\
& b \\leq 1.0\\\\
& c \\leq 1.0\\\\
& b1 \\leq 1.0\\\\
& c1 \\leq 1.0\\\\
& a + b - 10 c - 2 x + c1 \\leq 1.0\\\\
& a\\times b \\leq 2.0\\\\
& [-a + 1, u_{1}, u_{2}, u_{3}] \\in MathOptInterface.SecondOrderCone(4)\\\\
\\end{alignat*}
""")

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

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

io_test(REPLMode, model_2, """
A JuMP Model
Feasibility problem with:
Variables: 2
`MathOptInterface.SingleVariable`-in-`MathOptInterface.ZeroOne`: 1 constraint
`MathOptInterface.SingleVariable`-in-`MathOptInterface.Integer`: 1 constraint
`MathOptInterface.ScalarQuadraticFunction{Float64}`-in-`MathOptInterface.LessThan{Float64}`: 1 constraint
Model mode: Automatic
CachingOptimizer state: NoOptimizer
Solver name: No optimizer attached.
Names registered in the model: y, x""", repl=:show)

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

io_test(REPLMode, model_2, """
A JuMP Model
Feasibility problem with:
Variable: 1
`MathOptInterface.ScalarAffineFunction{Float64}`-in-`MathOptInterface.LessThan{Float64}`: 1 constraint
Model mode: Automatic
CachingOptimizer state: NoOptimizer
Solver name: No optimizer attached.
Names registered in the model: x""", repl=:show)
end
end

@testset "Printing for JuMP.Model" begin
Expand Down

0 comments on commit 2ad444d

Please sign in to comment.