TypeError when using Symbolics.jl with Polynomials.jl

I’m new to the Symbolics.jl package, and I’m currently trying to use it in combination with other packages. However I keep getting the error ERROR: TypeError: non-boolean (Num) used in boolean context when I try to use symbolic variables with a couple other packages.

For example, I’ve tried with the Polynomials.jl package: (Full stacktrace at the bottom of this post for visual clarity.)

julia> using Polynomials, Symbolics

julia> @variables x
(x,)

julia> Polynomial([x, 1, 2])
Polynomial(Error showing value of type Polynomial{Num}:
ERROR: TypeError: non-boolean (Num) used in boolean context
Stacktrace:
...

I’ve also tried with the ControlSystems.jl package (creating a transfer function), and I get the same error:

julia> using ControlSystems, Symbolics

julia> @variables x
(x,)

julia> tf([1],[x,1])
TransferFunction{Continuous, ControlSystems.SisoRational{Num}}
Error showing value of type TransferFunction{Continuous, ControlSystems.SisoRational{Num}}:
ERROR: TypeError: non-boolean (Num) used in boolean context
Stacktrace:
...

Would someone be willing to explain what the cause of this issue might be?

Julia version Info:

julia> versioninfo()
Julia Version 1.6.0-rc3
Commit 23267f0d46 (2021-03-16 17:04 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-11.0.1 (ORCJIT, skylake)

Package versions:

  • Symbolics.jl:
(practice1) pkg> st --manifest Symbolics
      Status `~/code3/julia/AI/practice1/Manifest.toml`
  [0c5d862f] Symbolics v0.1.9
  • Polynomials.jl:
(practice1) pkg> st --manifest Polynomials
      Status `~/code3/julia/AI/practice1/Manifest.toml`
  [f27b6e38] Polynomials v1.2.0
  • ControlSystems.jl:
(practice1) pkg> st --manifest ControlSystems
      Status `~/code3/julia/AI/practice1/Manifest.toml`
  [a6e380b2] ControlSystems v0.9.2

Stacktraces:

  • For the Polynomial.jl example:
julia> using Polynomials, Symbolics                                                                                                                                                                        
                                                                                                                                                                                                           
julia> @variables x
(x,)

julia> Polynomial([x, 1, 2])
Polynomial(Error showing value of type Polynomial{Num}:
ERROR: TypeError: non-boolean (Num) used in boolean context
Stacktrace:
  [1] printsign(io::IOContext{Base.TTY}, pj::Num, first::Bool, mimetype::MIME{Symbol("text/plain")})
    @ Polynomials ~/.julia/packages/Polynomials/OvhiS/src/show.jl:168
  [2] showterm(io::IOContext{Base.TTY}, #unused#::Type{Polynomial{Num}}, pj::Num, var::Symbol, j::Int64, first::Bool, mimetype::MIME{Symbol("text/plain")})
    @ Polynomials ~/.julia/packages/Polynomials/OvhiS/src/polynomials/standard-basis.jl:9
  [3] printpoly(io::IOContext{Base.TTY}, p::Polynomial{Num}, mimetype::MIME{Symbol("text/plain")}; descending_powers::Bool, offset::Int64, var::Symbol, compact::Bool, mulsymbol::String)
    @ Polynomials ~/.julia/packages/Polynomials/OvhiS/src/show.jl:145
  [4] printpoly
    @ ~/.julia/packages/Polynomials/OvhiS/src/show.jl:138 [inlined]
  [5] show(io::IOContext{Base.TTY}, mimetype::MIME{Symbol("text/plain")}, p::Polynomial{Num})
    @ Polynomials ~/.julia/packages/Polynomials/OvhiS/src/show.jl:56
  [6] (::REPL.var"#38#39"{REPL.REPLDisplay{REPL.LineEditREPL}, MIME{Symbol("text/plain")}, Base.RefValue{Any}})(io::Any)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:220
  [7] with_repl_linfo(f::Any, repl::REPL.LineEditREPL)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:462
  [8] display(d::REPL.REPLDisplay, mime::MIME{Symbol("text/plain")}, x::Any)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:213
  [9] display(d::REPL.REPLDisplay, x::Any)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:225
 [10] display(x::Any)
    @ Base.Multimedia ./multimedia.jl:328
 [11] #invokelatest#2
    @ ./essentials.jl:708 [inlined]
 [12] invokelatest
    @ ./essentials.jl:706 [inlined]
 [13] print_response(errio::IO, response::Any, show_value::Bool, have_color::Bool, specialdisplay::Union{Nothing, AbstractDisplay})
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:247
 [14] (::REPL.var"#40#41"{REPL.LineEditREPL, Pair{Any, Bool}, Bool, Bool})(io::Any)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:231
 [15] with_repl_linfo(f::Any, repl::REPL.LineEditREPL)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:462
 [16] print_response(repl::REPL.AbstractREPL, response::Any, show_value::Bool, have_color::Bool)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:229
 [17] (::REPL.var"#do_respond#61"{Bool, Bool, REPL.var"#72#82"{REPL.LineEditREPL, REPL.REPLHistoryProvider}, REPL.LineEditREPL, REPL.LineEdit.Prompt})(s::REPL.LineEdit.MIState, buf::Any, ok::Bool)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:798
 [18] #invokelatest#2
    @ ./essentials.jl:708 [inlined]
 [19] invokelatest
    @ ./essentials.jl:706 [inlined]
 [20] run_interface(terminal::REPL.Terminals.TextTerminal, m::REPL.LineEdit.ModalInterface, s::REPL.LineEdit.MIState)
    @ REPL.LineEdit /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/LineEdit.jl:2441
 [21] run_frontend(repl::REPL.LineEditREPL, backend::REPL.REPLBackendRef)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:1126
 [22] (::REPL.var"#44#49"{REPL.LineEditREPL, REPL.REPLBackendRef})()
    @ REPL ./task.jl:406

  • For the ControlSystems.jl example:
julia> using ControlSystems, Symbolics                                                                                                                                                                     
                                                                                                                                                                                                           
julia> @variables x                                                                                                                                                                                        
(x,)                                                                                                                                                                                                       
                                                                                                                                                                                                           
julia> tf([1],[x,1])                                                                                                                                                                                       
TransferFunction{Continuous, ControlSystems.SisoRational{Num}}                                                                                                                                             
Error showing value of type TransferFunction{Continuous, ControlSystems.SisoRational{Num}}:                                                                                                                
ERROR: TypeError: non-boolean (Num) used in boolean context                                                                                                                                                
Stacktrace:
  [1] printsign(io::IOContext{IOBuffer}, pj::Num, first::Bool, mimetype::MIME{Symbol("text/plain")})                                                                                                       
    @ Polynomials ~/.julia/packages/Polynomials/OvhiS/src/show.jl:168
  [2] showterm(io::IOContext{IOBuffer}, #unused#::Type{Polynomial{Num}}, pj::Num, var::Symbol, j::Int64, first::Bool, mimetype::MIME{Symbol("text/plain")})
    @ Polynomials ~/.julia/packages/Polynomials/OvhiS/src/polynomials/standard-basis.jl:9
  [3] printpoly(io::IOBuffer, p::Polynomial{Num}, mimetype::MIME{Symbol("text/plain")}; descending_powers::Bool, offset::Int64, var::Symbol, compact::Bool, mulsymbol::String)
    @ Polynomials ~/.julia/packages/Polynomials/OvhiS/src/show.jl:145
  [4] #90 (repeats 2 times)
    @ ~/.julia/packages/ControlSystems/D98Nv/src/utilities.jl:57 [inlined]
  [5] sprint(f::Function, args::Polynomial{Num}; context::Nothing, sizehint::Int64)
    @ Base ./strings/io.jl:105
  [6] sprint
    @ ./strings/io.jl:101 [inlined]
  [7] print_siso(io::IOContext{Base.TTY}, f::ControlSystems.SisoRational{Num}, var::Symbol)
    @ ControlSystems ~/.julia/packages/ControlSystems/D98Nv/src/types/SisoTfTypes/SisoRational.jl:48
  [8] show(io::IOContext{Base.TTY}, G::TransferFunction{Continuous, ControlSystems.SisoRational{Num}})
    @ ControlSystems ~/.julia/packages/ControlSystems/D98Nv/src/types/TransferFunction.jl:174
  [9] show(io::IOContext{Base.TTY}, #unused#::MIME{Symbol("text/plain")}, x::TransferFunction{Continuous, ControlSystems.SisoRational{Num}})
    @ Base.Multimedia ./multimedia.jl:47
 [10] (::REPL.var"#38#39"{REPL.REPLDisplay{REPL.LineEditREPL}, MIME{Symbol("text/plain")}, Base.RefValue{Any}})(io::Any)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:220
 [11] with_repl_linfo(f::Any, repl::REPL.LineEditREPL)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:462
 [12] display(d::REPL.REPLDisplay, mime::MIME{Symbol("text/plain")}, x::Any)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:213
 [13] display(d::REPL.REPLDisplay, x::Any)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:225
 [14] display(x::Any)
    @ Base.Multimedia ./multimedia.jl:328
 [15] #invokelatest#2
    @ ./essentials.jl:708 [inlined]
 [16] invokelatest
    @ ./essentials.jl:706 [inlined]
 [17] print_response(errio::IO, response::Any, show_value::Bool, have_color::Bool, specialdisplay::Union{Nothing, AbstractDisplay})
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:247
 [18] (::REPL.var"#40#41"{REPL.LineEditREPL, Pair{Any, Bool}, Bool, Bool})(io::Any)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:231
 [19] with_repl_linfo(f::Any, repl::REPL.LineEditREPL)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:462
 [20] print_response(repl::REPL.AbstractREPL, response::Any, show_value::Bool, have_color::Bool)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:229
 [21] (::REPL.var"#do_respond#61"{Bool, Bool, REPL.var"#72#82"{REPL.LineEditREPL, REPL.REPLHistoryProvider}, REPL.LineEditREPL, REPL.LineEdit.Prompt})(s::REPL.LineEdit.MIState, buf::Any, ok::Bool)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:798
 [22] #invokelatest#2
    @ ./essentials.jl:708 [inlined]
 [23] invokelatest
    @ ./essentials.jl:706 [inlined]
 [24] run_interface(terminal::REPL.Terminals.TextTerminal, m::REPL.LineEdit.ModalInterface, s::REPL.LineEdit.MIState)
    @ REPL.LineEdit /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/LineEdit.jl:2441
 [25] run_frontend(repl::REPL.LineEditREPL, backend::REPL.REPLBackendRef)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:1126
 [26] (::REPL.var"#44#49"{REPL.LineEditREPL, REPL.REPLBackendRef})()
    @ REPL ./task.jl:406

For the Polynomials case, the issue arises because Num <: Real., and so Polynomials.hasneg(Num) = true. This in turn means that isneg(x::Num) = x < zero(Num), which is expected to be a Bool, but actually gets turned into another Num that encodes the comparison. All of this logic happens while trying to pretty print the Polynomial. It is possible that only the printing is broken by this type of issue, but I haven’t checked.

This is a tricky problem. On the one hand, it seems like code ought to be able to assume that logical comparisons of subtypes of Real will yield a Bool, but on the other hand, the value of x is unknown, so Symbolics can’t truthfully make the comparison until a value for x is specified.

2 Likes

[Edit, sorry, I see adamsic beat me to much of this…]

For Polynomials.jl the issue is the use of conditional formatting based on the values of a coefficient. E.g. https://github.com/JuliaMath/Polynomials.jl/blob/master/src/polynomials/standard-basis.jl#L5

There we check if the coefficient is zero, which for a symbolic value is not a Boolean, rather a symbolic expression. For that particular one, we could write x === zero(x), perhaps a pattern that would also work for other number types.

In SymPy, that is essentially how iszero is defined. SymPy has two different ways to test for zeroness: sympy has Eq(x,0) and for julia use there is iszero(x). The latter intended to be used generically, the former for symbolic treatment.

1 Like

Thanks for those great explanations. It looks like the error for the ControlSystems.jl might stem from the Polynomials.jl package as well. I’ll post this as an issue on Github.

Just speculating, but for printing purposes I wonder if it would be helpful (or possible) to distinguish between the sign of the unevaluated variable and the sign of the evaluated variable. For example, the unevaluated term -x could be considered “negative” for printing purposes, even though evaluating -x at any negative value of x results in a positive number.

As an update, I’m able to put Symbolic variables in Polynomials if all output is suppressed. For example, the following works just fine:

julia> using Polynomials, Symbolics

julia> @variables x y
(x, y)

julia> z = Polynomial([x, y, 1]); # Note the semicolon

julia> z[0]
x

julia> z[1]
y
1 Like

This is addressed here: https://github.com/JuliaMath/Polynomials.jl/pull/321

After this is merged, you might still wish to add a method to Base.show_unquoted, as mentioned in ?Polynomials.printcoefficient:

julia> @variables a b c
Poly(a, b, c)

julia> Polynomial([c,b,a])
Polynomial(c + b*x + a*x^2)

julia> Polynomial([c,b,a]) + Polynomial([a,b,c])
Polynomial(a + c + 2b*x + a + c*x^2)

julia> function Base.show_unquoted(io::IO, pj::Num, indent::Int, prec::Int)
              if Base.operator_precedence(:+) <= prec
                   print(io, "(")
                   show(io, pj)
                   print(io, ")")
               else
                   show(io, pj)
               end
           end

julia> Polynomial([c,b,a]) + Polynomial([a,b,c])
Polynomial((a + c) + (2b)*x + (a + c)*x^2)
3 Likes