Skip to content

Commit

Permalink
Merge pull request #1352 from JuliaOpt/bl/printunit
Browse files Browse the repository at this point in the history
Fix printing with Unitful coefficient
  • Loading branch information
blegat authored Jun 21, 2018
2 parents 75258ea + c9594a1 commit 13d109a
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 12 deletions.
28 changes: 16 additions & 12 deletions src/print.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ abstract type REPLMode <: PrintMode end
abstract type IJuliaMode <: PrintMode end

# Whether something is zero or not for the purposes of printing it
const PRINT_ZERO_TOL = 1e-10
# oneunit is useful e.g. if coef is a Unitful quantity
iszeroforprinting(coef) = abs(coef) < 1e-10 * oneunit(coef)
# Whether something is one or not for the purposes of printing it
isoneforprinting(coef) = iszeroforprinting(abs(coef) - oneunit(coef))
str_sign(coef) = coef < zero(coef) ? " - " : " + "

# List of indices available for variable printing
const DIMS = ["i","j","k","l","m","n"]
Expand All @@ -37,10 +41,11 @@ const DIMS = ["i","j","k","l","m","n"]
# e.g. 5.3 => 5.3
# 1.0 => 1
function str_round(f::Float64)
abs(f) == 0.0 && return "0" # strip sign off zero
iszero(f) && return "0" # strip sign off zero
str = string(f)
length(str) >= 2 && str[end-1:end] == ".0" ? str[1:end-2] : str
end
str_round(f) = string(f)

# TODO: get rid of this! This is only a helper, and should be Base.values
# (and probably live there, as well)
Expand Down Expand Up @@ -131,7 +136,6 @@ function var_str(::Type{IJuliaMode}, v::AbstractVariableRef; mathmode=true)
end
end


Base.show(io::IO, a::GenericAffExpr) = print(io, aff_str(REPLMode,a))
Base.show(io::IO, ::MIME"text/latex", a::GenericAffExpr) =
print(io, math(aff_str(IJuliaMode,a),false))
Expand All @@ -146,11 +150,11 @@ function aff_str(mode, a::GenericAffExpr{C, V}, show_constant=true) where {C, V}
elm = 1
# For each non-zero for this model
for (coef, var) in linearterms(a)
abs(coef) < PRINT_ZERO_TOL && continue # e.g. x - x
iszeroforprinting(coef) && continue # e.g. x - x

pre = abs(abs(coef)-1) < PRINT_ZERO_TOL ? "" : str_round(abs(coef)) * " "
pre = isoneforprinting(coef) ? "" : str_round(abs(coef)) * " "

term_str[2*elm-1] = coef < 0 ? " - " : " + "
term_str[2*elm-1] = str_sign(coef)
term_str[2*elm ] = string(pre, var_str(mode, var))
elm += 1
end
Expand All @@ -163,8 +167,8 @@ function aff_str(mode, a::GenericAffExpr{C, V}, show_constant=true) where {C, V}
# Correction for very first term - don't want a " + "/" - "
term_str[1] = (term_str[1] == " - ") ? "-" : ""
ret = join(term_str[1:2*(elm-1)])
if abs(a.constant) >= PRINT_ZERO_TOL && show_constant
ret = string(ret, a.constant < 0 ? " - " : " + ", str_round(abs(a.constant)))
if !iszeroforprinting(a.constant) && show_constant
ret = string(ret, str_sign(a.constant), str_round(abs(a.constant)))
end
return ret
end
Expand All @@ -191,19 +195,19 @@ function quad_str(mode, q::GenericQuadExpr, sym)
elm = 1
if length(term_str) > 0
for (coef, var1, var2) in quadterms(q)
abs(coef) < PRINT_ZERO_TOL && continue # e.g. x - x
iszeroforprinting(coef) && continue # e.g. x - x

pre = abs(abs(coef)-1) < PRINT_ZERO_TOL ? "" : str_round(abs(coef)) * " "
pre = isoneforprinting(coef) ? "" : str_round(abs(coef)) * " "

x = var_str(mode,var1)
y = var_str(mode,var2)

term_str[2*elm-1] = coef < 0 ? " - " : " + "
term_str[2*elm-1] = str_sign(coef)
term_str[2*elm ] = "$pre$x" * (x == y ? sym[:sq] : "$(sym[:times])$y")
if elm == 1
# Correction for first term as there is no space
# between - and variable coefficient/name
term_str[1] = coef < 0 ? "-" : ""
term_str[1] = coef < zero(coef) ? "-" : ""
end
elm += 1
end
Expand Down
25 changes: 25 additions & 0 deletions test/print.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ function io_test(mode, obj, exp_str; repl=:both)
end
end

# Used to test that JuMP printing works correctly for types for which
# oneunit is not convertible to Float64
struct UnitNumber <: Number
α::Float64
end
Base.zero(::Union{UnitNumber, Type{UnitNumber}}) = UnitNumber(0.0)
Base.oneunit(::Union{UnitNumber, Type{UnitNumber}}) = UnitNumber(1.0)
Base.:(+)(u::UnitNumber, v::UnitNumber) = UnitNumber(u.α + v.α)
Base.:(-)(u::UnitNumber, v::UnitNumber) = UnitNumber(u.α - v.α)
Base.:(*)(α::Float64, u::UnitNumber) = UnitNumber* u.α)
Base.abs(u::UnitNumber) = UnitNumber(abs(u.α))
Base.isless(u::UnitNumber, v::UnitNumber) = isless(u.α, v.α)

@testset "Printing" begin

Expand Down Expand Up @@ -144,4 +156,17 @@ end
io_test(IJuliaMode, w[1,3], "symm_{1,3}")
end

# See https://github.com/JuliaOpt/JuMP.jl/pull/1352
@testset "Expression of coefficient type with unit" begin
m = Model()
@variable m x
@variable m y
u = UnitNumber(2.0)
aff = JuMP.GenericAffExpr(zero(u), x => u, y => zero(u))
io_test(REPLMode, aff, "UnitNumber(2.0) x")
io_test(IJuliaMode, aff, "UnitNumber(2.0) x")
quad = aff * x
io_test(REPLMode, quad, "UnitNumber(2.0) x² + UnitNumber(0.0)")
io_test(IJuliaMode, quad, "UnitNumber(2.0) x^2 + UnitNumber(0.0)")
end
end

0 comments on commit 13d109a

Please sign in to comment.