From ae36b2e60cfbadaa43a3c314852c898226ba704b Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 16 Nov 2023 16:58:45 +1300 Subject: [PATCH 01/10] Truncate printing of large expressions --- src/print.jl | 39 +++++++++++++++++++++++++++++++++++++-- test/test_print.jl | 20 ++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/print.jl b/src/print.jl index c48269fafb1..eb39963394b 100644 --- a/src/print.jl +++ b/src/print.jl @@ -605,6 +605,41 @@ function _term_string(coef, factor) end end +""" + const TERM_LIMIT_FOR_PRINTING = Ref{Int}(60) + +A global constant used to control when terms are omitted when printing +expressions. + +Get and set this value using `TERM_LIMIT_FOR_PRINTING[]`. + +```jldoctest +julia> TERM_LIMIT_FOR_PRINTING[] +60 + +julia> TERM_LIMIT_FOR_PRINTING[] = 10 +10 +``` +""" +const TERM_LIMIT_FOR_PRINTING = Ref{Int}(60) + +_terms_omitted(::MIME, n::Int) = "[[...$n terms omitted...]]" + +function _terms_omitted(::MIME"text/latex", n::Int) + return "[[\\ldots\\text{$n terms omitted}\\ldots]]" +end + +function _terms_to_truncated_string(mode, terms) + m = TERM_LIMIT_FOR_PRINTING[] + if length(terms) <= m + return join(terms) + end + k_l = iseven(m) ? m + 1 : m + 2 + k_r = iseven(m) ? m - 1 : m - 2 + block = _terms_omitted(mode, div(length(terms), 2) - m) + return string(join(terms[1:k_l]), block, join(terms[(end-k_r):end])) +end + # TODO(odow): remove show_constant in JuMP 1.0 function function_string(mode, a::GenericAffExpr, show_constant = true) if length(linear_terms(a)) == 0 @@ -616,7 +651,7 @@ function function_string(mode, a::GenericAffExpr, show_constant = true) terms[2*elm] = _term_string(coef, function_string(mode, var)) end terms[1] = terms[1] == " - " ? "-" : "" - ret = join(terms) + ret = _terms_to_truncated_string(mode, terms) if show_constant && !_is_zero_for_printing(a.constant) ret = string( ret, @@ -645,7 +680,7 @@ function function_string(mode, q::GenericQuadExpr) terms[2*elm] = _term_string(coef, factor) end terms[1] = terms[1] == " - " ? "-" : "" - ret = join(terms) + ret = _terms_to_truncated_string(mode, terms) aff_str = function_string(mode, q.aff) if aff_str == "0" return ret diff --git a/test/test_print.jl b/test/test_print.jl index 761f5776e69..c55346a3122 100644 --- a/test/test_print.jl +++ b/test/test_print.jl @@ -967,4 +967,24 @@ function test_print_text_latex_interval_set() return end +function test_truncated_printing() + model = Model() + @variable(model, x[1:1000]) + y = sum(x) + s = function_string(MIME("text/plain"), y) + @test occursin("x[30] + [[...940 terms omitted...]] + x[971]", s) + @test occursin( + "x_{30} + [[\\ldots\\text{940 terms omitted}\\ldots]] + x_{971}", + function_string(MIME("text/latex"), y), + ) + ret = TERM_LIMIT_FOR_PRINTING[] + TERM_LIMIT_FOR_PRINTING[] = 3 + @test function_string(MIME("text/plain"), y) == + "x[1] + x[2] + [[...997 terms omitted...]] + x[1000]" + @test function_string(MIME("text/latex"), y) == + "x_{1} + x_{2} + [[\\ldots\\text{997 terms omitted}\\ldots]] + x_{1000}" + TERM_LIMIT_FOR_PRINTING[] = ret + return +end + end From 8e4c59c908f741135ce51dba64edca35468f8932 Mon Sep 17 00:00:00 2001 From: odow Date: Fri, 17 Nov 2023 11:00:21 +1300 Subject: [PATCH 02/10] Update --- src/nlp_expr.jl | 90 ++++++++++++++++--------------------------- test/test_nlp_expr.jl | 15 ++++++++ 2 files changed, 49 insertions(+), 56 deletions(-) diff --git a/src/nlp_expr.jl b/src/nlp_expr.jl index fbe7e631753..543a90ff77c 100644 --- a/src/nlp_expr.jl +++ b/src/nlp_expr.jl @@ -144,79 +144,57 @@ function _needs_parentheses(x::GenericNonlinearExpr) return x.head in _PREFIX_OPERATORS && length(x.args) > 1 end -function function_string(::MIME"text/plain", x::GenericNonlinearExpr) - io, stack = IOBuffer(), Any[x] - while !isempty(stack) - arg = pop!(stack) - if arg isa GenericNonlinearExpr - if arg.head in _PREFIX_OPERATORS && length(arg.args) > 1 - if _needs_parentheses(arg.args[1]) - print(io, "(") - end - if _needs_parentheses(arg.args[end]) - push!(stack, ")") - end - for i in length(arg.args):-1:2 - push!(stack, arg.args[i]) - if _needs_parentheses(arg.args[i]) - push!(stack, "(") - end - push!(stack, " $(arg.head) ") - if _needs_parentheses(arg.args[i-1]) - push!(stack, ")") - end - end - push!(stack, arg.args[1]) - else - print(io, arg.head, "(") - push!(stack, ")") - for i in length(arg.args):-1:2 - push!(stack, arg.args[i]) - push!(stack, ", ") - end - if length(arg.args) >= 1 - push!(stack, arg.args[1]) - end - end - else - print(io, arg) - end - end - seekstart(io) - return read(io, String) -end +_parens(::MIME) = "(", ")", "", "", "" +_parens(::MIME"text/latex") = "\\left(", "\\right)", "{", "}", "\\textsf" -function function_string(::MIME"text/latex", x::GenericNonlinearExpr) +function function_string(mime::MIME, x::GenericNonlinearExpr) + p_left, p_right, p_open, p_close, p_textsf = _parens(mime) io, stack = IOBuffer(), Any[x] while !isempty(stack) arg = pop!(stack) if arg isa GenericNonlinearExpr if arg.head in _PREFIX_OPERATORS && length(arg.args) > 1 - print(io, "{") - push!(stack, "}") + print(io, p_open) + push!(stack, p_close) if _needs_parentheses(arg.args[1]) - print(io, "\\left(") + print(io, p_left) end if _needs_parentheses(arg.args[end]) - push!(stack, "\\right)") + push!(stack, p_right) end + l = ceil(TERM_LIMIT_FOR_PRINTING[] / 2) + r = floor(TERM_LIMIT_FOR_PRINTING[] / 2) + truncated_indices = (1+l):(length(arg.args) - r) for i in length(arg.args):-1:2 - push!(stack, arg.args[i]) - if _needs_parentheses(arg.args[i]) - push!(stack, "\\left(") - end - push!(stack, "} $(arg.head) {") - if _needs_parentheses(arg.args[i-1]) - push!(stack, "\\right)") + if i in truncated_indices + if i == truncated_indices[end] + push!( + stack, + _terms_omitted(mime, length(truncated_indices)), + ) + push!(stack, "$p_close $(arg.head) $p_open") + if _needs_parentheses(arg.args[i-1]) + push!(stack, p_right) + end + end + else + push!(stack, arg.args[i]) + if _needs_parentheses(arg.args[i]) + push!(stack, p_left) + end + push!(stack, "$p_close $(arg.head) $p_open") + if _needs_parentheses(arg.args[i-1]) + push!(stack, p_right) + end end end push!(stack, arg.args[1]) else - print(io, "\\textsf{", arg.head, "}\\left({") - push!(stack, "}\\right)") + print(io, p_textsf, p_open, arg.head, p_close, p_left, p_open) + push!(stack, p_right) for i in length(arg.args):-1:2 push!(stack, arg.args[i]) - push!(stack, "}, {") + push!(stack, "$p_close, $p_open") end if length(arg.args) >= 1 push!(stack, arg.args[1]) diff --git a/test/test_nlp_expr.jl b/test/test_nlp_expr.jl index def51c63ff1..da379537f47 100644 --- a/test/test_nlp_expr.jl +++ b/test/test_nlp_expr.jl @@ -978,4 +978,19 @@ function test_variable_ref_type() return end +function test_printing_truncation() + model = Model() + @variable(model, x[1:100]) + y = @expression(model, sum(sin.(x).*2)) + @test occursin( + "(sin(x[97]) * 2.0) + [[...91 terms omitted...]]) + (sin(x[5]) * 2.0)", + function_string(MIME("text/plain"), y), + ) + @test occursin( + "{\\left({\\textsf{sin}\\left({x[97]\\right)} * {2.0}\\right)} + {[[\\ldots\\text{91 terms omitted}\\ldots]]\\right)} + {\\left({\\textsf{sin}\\left({x[5]\\right)} * {2.0}\\right)}", + function_string(MIME("text/latex"), y), + ) + return +end + end # module From ba93210c37a5f9836f226f6a29f8d63d83bae9fe Mon Sep 17 00:00:00 2001 From: odow Date: Fri, 17 Nov 2023 11:05:58 +1300 Subject: [PATCH 03/10] Fix formatting --- src/nlp_expr.jl | 2 +- test/test_nlp_expr.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nlp_expr.jl b/src/nlp_expr.jl index 543a90ff77c..2d9c7674dbb 100644 --- a/src/nlp_expr.jl +++ b/src/nlp_expr.jl @@ -164,7 +164,7 @@ function function_string(mime::MIME, x::GenericNonlinearExpr) end l = ceil(TERM_LIMIT_FOR_PRINTING[] / 2) r = floor(TERM_LIMIT_FOR_PRINTING[] / 2) - truncated_indices = (1+l):(length(arg.args) - r) + truncated_indices = (1+l):(length(arg.args)-r) for i in length(arg.args):-1:2 if i in truncated_indices if i == truncated_indices[end] diff --git a/test/test_nlp_expr.jl b/test/test_nlp_expr.jl index da379537f47..300c4dbd775 100644 --- a/test/test_nlp_expr.jl +++ b/test/test_nlp_expr.jl @@ -981,7 +981,7 @@ end function test_printing_truncation() model = Model() @variable(model, x[1:100]) - y = @expression(model, sum(sin.(x).*2)) + y = @expression(model, sum(sin.(x) .* 2)) @test occursin( "(sin(x[97]) * 2.0) + [[...91 terms omitted...]]) + (sin(x[5]) * 2.0)", function_string(MIME("text/plain"), y), From f51a9133c5a731f17d74ec9797eebb0334465403 Mon Sep 17 00:00:00 2001 From: odow Date: Fri, 17 Nov 2023 11:32:42 +1300 Subject: [PATCH 04/10] Fix --- src/nlp_expr.jl | 2 +- src/print.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nlp_expr.jl b/src/nlp_expr.jl index 2d9c7674dbb..17270867ec4 100644 --- a/src/nlp_expr.jl +++ b/src/nlp_expr.jl @@ -191,7 +191,7 @@ function function_string(mime::MIME, x::GenericNonlinearExpr) push!(stack, arg.args[1]) else print(io, p_textsf, p_open, arg.head, p_close, p_left, p_open) - push!(stack, p_right) + push!(stack, p_close * p_right) for i in length(arg.args):-1:2 push!(stack, arg.args[i]) push!(stack, "$p_close, $p_open") diff --git a/src/print.jl b/src/print.jl index eb39963394b..dd3c69e0b61 100644 --- a/src/print.jl +++ b/src/print.jl @@ -631,7 +631,7 @@ end function _terms_to_truncated_string(mode, terms) m = TERM_LIMIT_FOR_PRINTING[] - if length(terms) <= m + if length(terms) <= 2 * m return join(terms) end k_l = iseven(m) ? m + 1 : m + 2 From 1eabbd3063d5f31bd111c56be5d22c4f162f54dd Mon Sep 17 00:00:00 2001 From: odow Date: Fri, 17 Nov 2023 14:26:31 +1300 Subject: [PATCH 05/10] Fix --- src/print.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/print.jl b/src/print.jl index dd3c69e0b61..11525632e27 100644 --- a/src/print.jl +++ b/src/print.jl @@ -613,7 +613,7 @@ expressions. Get and set this value using `TERM_LIMIT_FOR_PRINTING[]`. -```jldoctest +```julia julia> TERM_LIMIT_FOR_PRINTING[] 60 From d4e14ffb436f476af4c6afce5c4f8f17fdb14834 Mon Sep 17 00:00:00 2001 From: odow Date: Fri, 17 Nov 2023 15:13:01 +1300 Subject: [PATCH 06/10] Update --- test/test_nlp_expr.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_nlp_expr.jl b/test/test_nlp_expr.jl index 300c4dbd775..2a12eed094f 100644 --- a/test/test_nlp_expr.jl +++ b/test/test_nlp_expr.jl @@ -983,11 +983,11 @@ function test_printing_truncation() @variable(model, x[1:100]) y = @expression(model, sum(sin.(x) .* 2)) @test occursin( - "(sin(x[97]) * 2.0) + [[...91 terms omitted...]]) + (sin(x[5]) * 2.0)", + "(sin(x[72]) * 2.0) + [[...41 terms omitted...]]) + (sin(x[30]) * 2.0)", function_string(MIME("text/plain"), y), ) @test occursin( - "{\\left({\\textsf{sin}\\left({x[97]\\right)} * {2.0}\\right)} + {[[\\ldots\\text{91 terms omitted}\\ldots]]\\right)} + {\\left({\\textsf{sin}\\left({x[5]\\right)} * {2.0}\\right)}", + "{\\left({\\textsf{sin}\\left({x[72]\\right)} * {2.0}\\right)} + {[[\\ldots\\text{41 terms omitted}\\ldots]]\\right)} + {\\left({\\textsf{sin}\\left({x[30]\\right)} * {2.0}\\right)}", function_string(MIME("text/latex"), y), ) return From 93fafc13ea15d921e89af1c922e21e1561105980 Mon Sep 17 00:00:00 2001 From: odow Date: Fri, 17 Nov 2023 15:55:28 +1300 Subject: [PATCH 07/10] Update --- src/nlp_expr.jl | 38 +++++++++++++------------------------- test/test_nlp_expr.jl | 4 ++-- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/src/nlp_expr.jl b/src/nlp_expr.jl index 17270867ec4..e45f8856d4f 100644 --- a/src/nlp_expr.jl +++ b/src/nlp_expr.jl @@ -156,39 +156,27 @@ function function_string(mime::MIME, x::GenericNonlinearExpr) if arg.head in _PREFIX_OPERATORS && length(arg.args) > 1 print(io, p_open) push!(stack, p_close) - if _needs_parentheses(arg.args[1]) - print(io, p_left) - end - if _needs_parentheses(arg.args[end]) - push!(stack, p_right) - end l = ceil(TERM_LIMIT_FOR_PRINTING[] / 2) r = floor(TERM_LIMIT_FOR_PRINTING[] / 2) - truncated_indices = (1+l):(length(arg.args)-r) - for i in length(arg.args):-1:2 - if i in truncated_indices - if i == truncated_indices[end] - push!( - stack, - _terms_omitted(mime, length(truncated_indices)), - ) - push!(stack, "$p_close $(arg.head) $p_open") - if _needs_parentheses(arg.args[i-1]) - push!(stack, p_right) - end + skip_indices = (1+l):(length(arg.args)-r) + for i in length(arg.args):-1:1 + if i in skip_indices + if i == skip_indices[end] + push!(stack, _terms_omitted(mime, length(skip_indices))) + push!(stack, " $(arg.head) $p_open") end + continue + elseif _needs_parentheses(arg.args[i]) + push!(stack, p_right) + push!(stack, arg.args[i]) + push!(stack, p_left) else push!(stack, arg.args[i]) - if _needs_parentheses(arg.args[i]) - push!(stack, p_left) - end + end + if i > 1 push!(stack, "$p_close $(arg.head) $p_open") - if _needs_parentheses(arg.args[i-1]) - push!(stack, p_right) - end end end - push!(stack, arg.args[1]) else print(io, p_textsf, p_open, arg.head, p_close, p_left, p_open) push!(stack, p_close * p_right) diff --git a/test/test_nlp_expr.jl b/test/test_nlp_expr.jl index 2a12eed094f..da60d3a13bd 100644 --- a/test/test_nlp_expr.jl +++ b/test/test_nlp_expr.jl @@ -983,11 +983,11 @@ function test_printing_truncation() @variable(model, x[1:100]) y = @expression(model, sum(sin.(x) .* 2)) @test occursin( - "(sin(x[72]) * 2.0) + [[...41 terms omitted...]]) + (sin(x[30]) * 2.0)", + "(sin(x[72]) * 2.0) + [[...41 terms omitted...]] + (sin(x[30]) * 2.0)", function_string(MIME("text/plain"), y), ) @test occursin( - "{\\left({\\textsf{sin}\\left({x[72]\\right)} * {2.0}\\right)} + {[[\\ldots\\text{41 terms omitted}\\ldots]]\\right)} + {\\left({\\textsf{sin}\\left({x[30]\\right)} * {2.0}\\right)}", + "{\\left({\\textsf{sin}\\left({x[72]}\\right)} * {2.0}\\right) + {[[\\ldots\\text{41 terms omitted}\\ldots]]} + {\\left({\\textsf{sin}\\left({x[30]}\\right)} * {2.0}\\right)} + {\\left({\\textsf{sin}\\left({x[29]}\\right)} * {2.0}\\right)} + {\\left({\\textsf{sin}\\left({x[28]}\\right)} * {2.0}\\right)} + {\\left({\\textsf{sin}\\left({x[27]}\\right)} * {2.0}\\right)} + {\\left({\\textsf{sin}\\left({x[26]}\\right)} * {2.0}\\right)}", function_string(MIME("text/latex"), y), ) return From 19affb81bcfba45d50fa4b38d6ada4945c0d0f4a Mon Sep 17 00:00:00 2001 From: odow Date: Fri, 17 Nov 2023 16:26:27 +1300 Subject: [PATCH 08/10] Fix formatting --- src/nlp_expr.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/nlp_expr.jl b/src/nlp_expr.jl index e45f8856d4f..05eee2d7c2f 100644 --- a/src/nlp_expr.jl +++ b/src/nlp_expr.jl @@ -162,7 +162,10 @@ function function_string(mime::MIME, x::GenericNonlinearExpr) for i in length(arg.args):-1:1 if i in skip_indices if i == skip_indices[end] - push!(stack, _terms_omitted(mime, length(skip_indices))) + push!( + stack, + _terms_omitted(mime, length(skip_indices)), + ) push!(stack, " $(arg.head) $p_open") end continue From 58058f860cd48a35239cc728eab8d561e65fdef6 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 27 Nov 2023 10:45:19 +1300 Subject: [PATCH 09/10] Make constant private --- src/nlp_expr.jl | 4 ++-- src/print.jl | 12 ++++++------ test/test_print.jl | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/nlp_expr.jl b/src/nlp_expr.jl index 05eee2d7c2f..fa075adccf3 100644 --- a/src/nlp_expr.jl +++ b/src/nlp_expr.jl @@ -156,8 +156,8 @@ function function_string(mime::MIME, x::GenericNonlinearExpr) if arg.head in _PREFIX_OPERATORS && length(arg.args) > 1 print(io, p_open) push!(stack, p_close) - l = ceil(TERM_LIMIT_FOR_PRINTING[] / 2) - r = floor(TERM_LIMIT_FOR_PRINTING[] / 2) + l = ceil(_TERM_LIMIT_FOR_PRINTING[] / 2) + r = floor(_TERM_LIMIT_FOR_PRINTING[] / 2) skip_indices = (1+l):(length(arg.args)-r) for i in length(arg.args):-1:1 if i in skip_indices diff --git a/src/print.jl b/src/print.jl index 11525632e27..5847ab3922b 100644 --- a/src/print.jl +++ b/src/print.jl @@ -606,22 +606,22 @@ function _term_string(coef, factor) end """ - const TERM_LIMIT_FOR_PRINTING = Ref{Int}(60) + const _TERM_LIMIT_FOR_PRINTING = Ref{Int}(60) A global constant used to control when terms are omitted when printing expressions. -Get and set this value using `TERM_LIMIT_FOR_PRINTING[]`. +Get and set this value using `_TERM_LIMIT_FOR_PRINTING[]`. ```julia -julia> TERM_LIMIT_FOR_PRINTING[] +julia> _TERM_LIMIT_FOR_PRINTING[] 60 -julia> TERM_LIMIT_FOR_PRINTING[] = 10 +julia> _TERM_LIMIT_FOR_PRINTING[] = 10 10 ``` """ -const TERM_LIMIT_FOR_PRINTING = Ref{Int}(60) +const _TERM_LIMIT_FOR_PRINTING = Ref{Int}(60) _terms_omitted(::MIME, n::Int) = "[[...$n terms omitted...]]" @@ -630,7 +630,7 @@ function _terms_omitted(::MIME"text/latex", n::Int) end function _terms_to_truncated_string(mode, terms) - m = TERM_LIMIT_FOR_PRINTING[] + m = _TERM_LIMIT_FOR_PRINTING[] if length(terms) <= 2 * m return join(terms) end diff --git a/test/test_print.jl b/test/test_print.jl index c55346a3122..e426e92dc87 100644 --- a/test/test_print.jl +++ b/test/test_print.jl @@ -977,13 +977,13 @@ function test_truncated_printing() "x_{30} + [[\\ldots\\text{940 terms omitted}\\ldots]] + x_{971}", function_string(MIME("text/latex"), y), ) - ret = TERM_LIMIT_FOR_PRINTING[] - TERM_LIMIT_FOR_PRINTING[] = 3 + ret = _TERM_LIMIT_FOR_PRINTING[] + _TERM_LIMIT_FOR_PRINTING[] = 3 @test function_string(MIME("text/plain"), y) == "x[1] + x[2] + [[...997 terms omitted...]] + x[1000]" @test function_string(MIME("text/latex"), y) == "x_{1} + x_{2} + [[\\ldots\\text{997 terms omitted}\\ldots]] + x_{1000}" - TERM_LIMIT_FOR_PRINTING[] = ret + _TERM_LIMIT_FOR_PRINTING[] = ret return end From 22bd2a40c8fa768a8bcfaf6a967aec04a1e810f6 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Mon, 27 Nov 2023 12:12:33 +1300 Subject: [PATCH 10/10] Apply suggestions from code review --- test/test_print.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/test_print.jl b/test/test_print.jl index e426e92dc87..fa0eb642c5c 100644 --- a/test/test_print.jl +++ b/test/test_print.jl @@ -977,13 +977,13 @@ function test_truncated_printing() "x_{30} + [[\\ldots\\text{940 terms omitted}\\ldots]] + x_{971}", function_string(MIME("text/latex"), y), ) - ret = _TERM_LIMIT_FOR_PRINTING[] - _TERM_LIMIT_FOR_PRINTING[] = 3 + ret = JuMP._TERM_LIMIT_FOR_PRINTING[] + JuMP._TERM_LIMIT_FOR_PRINTING[] = 3 @test function_string(MIME("text/plain"), y) == "x[1] + x[2] + [[...997 terms omitted...]] + x[1000]" @test function_string(MIME("text/latex"), y) == "x_{1} + x_{2} + [[\\ldots\\text{997 terms omitted}\\ldots]] + x_{1000}" - _TERM_LIMIT_FOR_PRINTING[] = ret + JuMP._TERM_LIMIT_FOR_PRINTING[] = ret return end