symengine / SymEngine.jl

Julia wrappers of SymEngine
MIT License
192 stars 43 forks source link

Segmentation fault and "Trying to print an uninitialized SymEngine Basic variable" when encountering `SymEngine.zoo` #228

Closed johanbluecreek closed 3 years ago

johanbluecreek commented 3 years ago

When I encounter SymEngine.zoo, I get into some problems. For example

$ julia
julia> using SymEngine
julia> @vars x
julia> exp(zoo + x)
exp(zoo + x)
julia> exp(zoo)
Error showing value of type Basic:
ERROR: Trying to print an uninitialized SymEngine Basic variable.
Stacktrace:
  [1] error(s::String)
    @ Base ./error.jl:33
  [2] toString(b::Basic)
    @ SymEngine ~/.julia/packages/SymEngine/Qs90w/src/display.jl:5
  [3] show
    @ ~/.julia/packages/SymEngine/Qs90w/src/display.jl:13 [inlined]
  [4] show(io::IOContext{Base.TTY}, #unused#::MIME{Symbol("text/plain")}, x::Basic)
    @ Base.Multimedia ./multimedia.jl:47
  [5] (::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
  [6] 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
  [7] 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
  [8] display(d::REPL.REPLDisplay, x::Any)
    @ REPL /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:225
  [9] display(x::Any)
    @ Base.Multimedia ./multimedia.jl:328
 [10] #invokelatest#2
    @ ./essentials.jl:708 [inlined]
 [11] invokelatest
    @ ./essentials.jl:706 [inlined]
 [12] 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
 [13] (::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
 [14] 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
 [15] 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
 [16] (::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
 [17] #invokelatest#2
    @ ./essentials.jl:708 [inlined]
 [18] invokelatest
    @ ./essentials.jl:706 [inlined]
 [19] 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
 [20] 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
 [21] (::REPL.var"#44#49"{REPL.LineEditREPL, REPL.REPLBackendRef})()
    @ REPL ./task.jl:411

This appears to be "only" a printing issue, since I'm tricked into thinking it is a valid expression (just not printable) by adding a semicolon

julia> exp(zoo);
julia>

Here comes the real issue: when I then try to work with this expression I get a segmentation fault

julia> a = exp(zoo);
julia> a + x
signal (11): Segmentation fault
in expression starting at REPL[7]:1
_ZN9SymEngine3addERKNS_3RCPIKNS_5BasicEEES5_ at /home/user/.julia/artifacts/0cb27ab7d326673371c8bcf4f7be984e22312bbb/lib/libsymengine.so (unknown line)
basic_add at /home/user/.julia/artifacts/0cb27ab7d326673371c8bcf4f7be984e22312bbb/lib/libsymengine.so (unknown line)
+ at /home/user/.julia/packages/SymEngine/Qs90w/src/mathops.jl:16
_jl_invoke at /buildworker/worker/package_linux64/build/src/gf.c:2237 [inlined]
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2419
jl_apply at /buildworker/worker/package_linux64/build/src/julia.h:1703 [inlined]
do_call at /buildworker/worker/package_linux64/build/src/interpreter.c:115
eval_value at /buildworker/worker/package_linux64/build/src/interpreter.c:204
eval_stmt_value at /buildworker/worker/package_linux64/build/src/interpreter.c:155 [inlined]
eval_body at /buildworker/worker/package_linux64/build/src/interpreter.c:562
jl_interpret_toplevel_thunk at /buildworker/worker/package_linux64/build/src/interpreter.c:670
jl_toplevel_eval_flex at /buildworker/worker/package_linux64/build/src/toplevel.c:877
jl_toplevel_eval_flex at /buildworker/worker/package_linux64/build/src/toplevel.c:825
jl_toplevel_eval_in at /buildworker/worker/package_linux64/build/src/toplevel.c:929
eval at ./boot.jl:360 [inlined]
eval_user_input at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:139
repl_backend_loop at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:200
start_repl_backend at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:185
#run_repl#42 at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:317
run_repl at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:305
_jl_invoke at /buildworker/worker/package_linux64/build/src/gf.c:2237 [inlined]
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2419
#874 at ./client.jl:387
jfptr_YY.874_23032.clone_1 at /home/user/opt/julia-1.6.2/lib/julia/sys.so (unknown line)
_jl_invoke at /buildworker/worker/package_linux64/build/src/gf.c:2237 [inlined]
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2419
jl_apply at /buildworker/worker/package_linux64/build/src/julia.h:1703 [inlined]
jl_f__call_latest at /buildworker/worker/package_linux64/build/src/builtins.c:714
#invokelatest#2 at ./essentials.jl:708 [inlined]
invokelatest at ./essentials.jl:706 [inlined]
run_main_repl at ./client.jl:372
exec_options at ./client.jl:302
_start at ./client.jl:485
jfptr__start_34281.clone_1 at /home/user/opt/julia-1.6.2/lib/julia/sys.so (unknown line)
_jl_invoke at /buildworker/worker/package_linux64/build/src/gf.c:2237 [inlined]
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2419
jl_apply at /buildworker/worker/package_linux64/build/src/julia.h:1703 [inlined]
true_main at /buildworker/worker/package_linux64/build/src/jlapi.c:560
repl_entrypoint at /buildworker/worker/package_linux64/build/src/jlapi.c:702
main at julia (unknown line)
__libc_start_main at /lib/x86_64-linux-gnu/libc.so.6 (unknown line)
unknown function (ip: 0x4007d8)
Allocations: 1525108 (Pool: 1524006; Big: 1102); GC: 2
Segmentation fault

It would be nice to have this throw an error instead of a segmentation fault. Perhaps even at the exp(zoo)-stage. Other functions behaves the same, e.g. sin(zoo) + x.

isuruf commented 3 years ago

Can you send a PR adding the test case? I can then work on throwing an error.

johanbluecreek commented 3 years ago

I'm not completely sure what you are asking me to do. Should I PR a test of the behaviour that I want SymEngine to have, but that does not yet work?

Since sin(Inf) throws DomainError, I suppose desired behaviour in SymEngine should be:

@test_throws DomainError exp(zoo)
@test_throws DomainError sin(zoo)
@test_throws DomainError sin(oo)

Should I put this in test/runtests.jl with a PR?

To explain the tests: In the above I test for both sin and exp just to make sure a future fix is not for just one function (needs to be a fix for any built-in function), and then I noticed that while e.g. exp(Inf) and exp(oo) has the same behaviour, sin(Inf) and sin(oo) does not (sin(oo) behaves like sin(zoo) in my first post), so that is the third test. If the behaviour was this, then the segmentation fault should be avoided.

isuruf commented 3 years ago

Should I PR a test of the behaviour that I want SymEngine to have, but that does not yet work?

Yes

Should I put this in test/runtests.jl with a PR?

Yes

johanbluecreek commented 3 years ago

Updated and tested the new version and it works great! Thank you for the fix. I'm closing this issue.

johanbluecreek commented 3 years ago

After some more exploring I found a way to trigger the same sort of segfault, so I'm reopening this issue (The problem seems to be in subs(), let me know if I should open a new issue instead). Consider this:

julia> using SymEngine
julia> @vars x,y
(x, y)
julia> subs(sin(zoo*x), x => 0.1)
Error showing value of type Basic:
ERROR: Trying to print an uninitialized SymEngine Basic variable.
[---]
julia> sin(zoo*0.1)
ERROR: DomainError with sin:
[---]
julia> subs(sin(zoo*x), x => 0.1) + y
signal (11): Segmentation fault
[---]

So while sin(zoo*0.1) seems to be parsed in the correct order (sin(zoo*0.1) -> sin(zoo) -> DomainError), using subs() does not, but causes a "uninitialized SymEngine Basic variable" instead (just like before) that can lead to a segfault.

A test avoiding this and demonstrating desired behaviour would be

@test_throws DomainError subs(sin(zoo*x), x => 0.1)

Should I make another PR for this?

johanbluecreek commented 3 years ago

There does not need to be an explicit zoo to cause this issue, as demonstrated by the following:

julia> subs(sin(log(y - y/x)), x => 1)
Error showing value of type Basic:
ERROR: Trying to print an uninitialized SymEngine Basic variable.
[---]
julia> subs(log(y - y/x), x => 1)
zoo
isuruf commented 3 years ago

Should I make another PR for this?

Yes please.

If you want to try a fix for this, it's going to be the same as the change I did. See https://github.com/symengine/SymEngine.jl/blob/master/src/subs.jl#L21

johanbluecreek commented 3 years ago

Thanks for the merge... I'm however back again because I found a similar issue but with diff(). This one is a bit different, since it does not cause segfaults, and the same fix does not work, but it still involves infinities.

The issue are expressions like exp(zoo+x), which does not throw DomainError, and you can cause problems by:

julia> using SymEngine
julia> @vars x
julia> exp(zoo+x)
exp(zoo+x)
julia> exp(zoo+x)/exp(x)
exp(zoo)
julia> exp(zoo+x)/exp(x) |> e -> diff(e, x)
terminate called after throwing an instance of 'SymEngine::DomainError'
  what():  exp is not defined for Complex Infinity

signal (6): Aborted
in expression starting at REPL[5]:1
gsignal at /lib/x86_64-linux-gnu/libc.so.6 (unknown line)
abort at /lib/x86_64-linux-gnu/libc.so.6 (unknown line)
__verbose_terminate_handler at /workspace/srcdir/gcc-9.1.0/libstdc++-v3/libsupc++/vterminate.cc:95
__terminate at /workspace/srcdir/gcc-9.1.0/libstdc++-v3/libsupc++/eh_terminate.cc:47
terminate at /workspace/srcdir/gcc-9.1.0/libstdc++-v3/libsupc++/eh_terminate.cc:57
__cxa_throw at /workspace/srcdir/gcc-9.1.0/libstdc++-v3/libsupc++/eh_throw.cc:95
_ZNK9SymEngine13EvaluateInfty3expERKNS_5BasicE at /home/user/.julia/artifacts/0cb27ab7d326673371c8bcf4f7be984e22312bbb/lib/libsymengine.so (unknown line)
_ZN9SymEngine3powERKNS_3RCPIKNS_5BasicEEES5_ at /home/user/.julia/artifacts/0cb27ab7d326673371c8bcf4f7be984e22312bbb/lib/libsymengine.so (unknown line)
_ZN9SymEngine11DiffVisitor6bvisitERKNS_3PowE at /home/user/.julia/artifacts/0cb27ab7d326673371c8bcf4f7be984e22312bbb/lib/libsymengine.so (unknown line)
_ZN9SymEngine11DiffVisitor5applyERKNS_3RCPIKNS_5BasicEEE at /home/user/.julia/artifacts/0cb27ab7d326673371c8bcf4f7be984e22312bbb/lib/libsymengine.so (unknown line)
_ZN9SymEngine4diffERKNS_3RCPIKNS_5BasicEEERKNS0_IKNS_6SymbolEEEb at /home/user/.julia/artifacts/0cb27ab7d326673371c8bcf4f7be984e22312bbb/lib/libsymengine.so (unknown line)
_ZNK9SymEngine5Basic4diffERKNS_3RCPIKNS_6SymbolEEEb at /home/user/.julia/artifacts/0cb27ab7d326673371c8bcf4f7be984e22312bbb/lib/libsymengine.so (unknown line)
basic_diff at /home/user/.julia/artifacts/0cb27ab7d326673371c8bcf4f7be984e22312bbb/lib/libsymengine.so (unknown line)
diff at /home/user/.julia/packages/SymEngine/8m4y8/src/calculus.jl:12
diff at /home/user/.julia/packages/SymEngine/8m4y8/src/calculus.jl:23
diff at /home/user/.julia/packages/SymEngine/8m4y8/src/calculus.jl:21
unknown function (ip: 0x7f9256c6ac5b)
_jl_invoke at /buildworker/worker/package_linux64/build/src/gf.c:2237 [inlined]
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2419
#1 at ./REPL[5]:1
|> at ./operators.jl:858
_jl_invoke at /buildworker/worker/package_linux64/build/src/gf.c:2237 [inlined]
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2419
jl_apply at /buildworker/worker/package_linux64/build/src/julia.h:1703 [inlined]
do_call at /buildworker/worker/package_linux64/build/src/interpreter.c:115
eval_value at /buildworker/worker/package_linux64/build/src/interpreter.c:204
eval_stmt_value at /buildworker/worker/package_linux64/build/src/interpreter.c:155 [inlined]
eval_body at /buildworker/worker/package_linux64/build/src/interpreter.c:562
jl_interpret_toplevel_thunk at /buildworker/worker/package_linux64/build/src/interpreter.c:670
jl_toplevel_eval_flex at /buildworker/worker/package_linux64/build/src/toplevel.c:877
jl_toplevel_eval_flex at /buildworker/worker/package_linux64/build/src/toplevel.c:825
jl_toplevel_eval_in at /buildworker/worker/package_linux64/build/src/toplevel.c:929
eval at ./boot.jl:360 [inlined]
eval_user_input at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:139
repl_backend_loop at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:200
start_repl_backend at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:185
#run_repl#42 at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:317
run_repl at /buildworker/worker/package_linux64/build/usr/share/julia/stdlib/v1.6/REPL/src/REPL.jl:305
_jl_invoke at /buildworker/worker/package_linux64/build/src/gf.c:2237 [inlined]
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2419
#874 at ./client.jl:387
jfptr_YY.874_23032.clone_1 at /home/user/opt/julia-1.6.2/lib/julia/sys.so (unknown line)
_jl_invoke at /buildworker/worker/package_linux64/build/src/gf.c:2237 [inlined]
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2419
jl_apply at /buildworker/worker/package_linux64/build/src/julia.h:1703 [inlined]
jl_f__call_latest at /buildworker/worker/package_linux64/build/src/builtins.c:714
#invokelatest#2 at ./essentials.jl:708 [inlined]
invokelatest at ./essentials.jl:706 [inlined]
run_main_repl at ./client.jl:372   
exec_options at ./client.jl:302
_start at ./client.jl:485
jfptr__start_34281.clone_1 at /home/user/opt/julia-1.6.2/lib/julia/sys.so (unknown line)
_jl_invoke at /buildworker/worker/package_linux64/build/src/gf.c:2237 [inlined]
jl_apply_generic at /buildworker/worker/package_linux64/build/src/gf.c:2419
jl_apply at /buildworker/worker/package_linux64/build/src/julia.h:1703 [inlined]
true_main at /buildworker/worker/package_linux64/build/src/jlapi.c:560
repl_entrypoint at /buildworker/worker/package_linux64/build/src/jlapi.c:702
main at julia (unknown line)
__libc_start_main at /lib/x86_64-linux-gnu/libc.so.6 (unknown line)
unknown function (ip: 0x4007d8)
Allocations: 1539110 (Pool: 1538040; Big: 1070); GC: 2
Aborted
$

Again, preferred behaviour would be to have it throw a DomainError at some point.

Since the issue is a bit different, do you want me to open a new one and close this one?

I guess the best test for this issue would be to have

@test_throws DomainError exp(zoo+x)/exp(x)

since exp(zoo) by itself throws DomainError, in that way one could avoid other yet-to-be-discovered instances (beyond diff()) where exp(zoo+x)/exp(x)-like expressions could cause problems. Do you want a PR with this test?