SymPy Plotting Backends で少し込み入った関数をplotするときのエラー対応

SymPy Plotting Backends で”少し込み入った関数”を plot() するとき,思いもかけずにエラーとなった場合の対応。
以下の Issue で回答があった件:

必要なモジュールの import

In [1]:
from sympy import *
from sympy.abc import *
from spb import *

# グラフを SVG で Notebook にインライン表示
%config InlineBackend.figure_formats = ['svg']

関数の定義の中に if 文がある例

実際に「補足:SymPy と SPB で宇宙年齢のグラフを描く」のページでエラーが出て困った。宇宙年齢は密度パラメータ $\Omega_{\rm m}$ の関数であるが,曲率定数 $k$ の符号によって関数形が変わる。そのため,1個の関数で全ての $k$ の場合の宇宙年齢を $\Omega_{\rm m}$ の関数としてあらわそうとすると,if 文が必要になり,最初なぜエラーになるのかわからなかったので,備忘録としてメモ。

例えば,以下のような例。

In [2]:
def f(x):
    if x < 0:
        return -x
    else:
        return x

以下のように plot() しようとするとエラーになる。

In [3]:
plot(f(x), (x, -2, 2));
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-3-2ab6b255ae28> in <module>
----> 1 plot(f(x), (x, -2, 2));

<ipython-input-2-c220f646b8f3> in f(x)
      1 def f(x):
----> 2     if x < 0:
      3         return -x
      4     else:
      5         return x

/usr/local/lib/python3.8/dist-packages/sympy/core/relational.py in __bool__(self)
    508 
    509     def __bool__(self):
--> 510         raise TypeError("cannot determine truth value of Relational")
    511 
    512     def _eval_as_set(self):

TypeError: cannot determine truth value of Relational

傾向と対策

通常ならば

plot(f(x), (x, -2, 2))

とするところだが,関数 f(x) の定義が少し込み入っていて if 文がある場合,これだとエラーになる。

対策は,

  • 関数名のみにする(かっこや引数は省略する)
  • force_real_eval=True のオプションをつける

つまり,以下のようにすればよい。

plot(f, (x, -2, 2), force_real_eval=True)
In [4]:
# plot(f(x), (x, -2, 2)) ではなくて...

plot(f, (x, -2, 2), force_real_eval=True);

関数の定義の中に数値解法の nsolve() がある例

例えば,以下のような例。

In [5]:
def g(x):
    return nsolve(sin(t)-x, t, 0)

以下のように plot() しようとするとエラーになる。

In [6]:
plot(g(x), (x, -1, 1));
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-6-060c36080d54> in <module>
----> 1 plot(g(x), (x, -1, 1));

<ipython-input-5-28e344be0bf6> in g(x)
      1 def g(x):
----> 2     return nsolve(sin(t)-x, t, 0)

/usr/local/lib/python3.8/dist-packages/sympy/utilities/decorator.py in func_wrapper(*args, **kwargs)
     85         dps = mpmath.mp.dps
     86         try:
---> 87             return func(*args, **kwargs)
     88         finally:
     89             mpmath.mp.dps = dps

/usr/local/lib/python3.8/dist-packages/sympy/solvers/solvers.py in nsolve(dict, *args, **kwargs)
   3033             fargs = syms.copy().pop()
   3034         if not (len(syms) == 1 and (fargs in syms or fargs[0] in syms)):
-> 3035             raise ValueError(filldedent('''
   3036                 expected a one-dimensional and numerical function'''))
   3037 

ValueError: 
expected a one-dimensional and numerical function

傾向と対策

通常ならば

plot(g(x), (x, -1, 1))

とするところだが,関数 g(x) の定義が少し込み入っていて nsolve() 文がある場合,これだとエラーになる。

対策は,

  • 関数名のみにする(かっこや引数は省略する)

つまり,以下のようにすればよい。

plot(g, (x, -1, 1))
In [7]:
# plot(g(x), (x, -1, 1)) ではなくて...

plot(g, (x, -1, 1));

参考:Maxima で少し込み入った関数をグラフにする

SymPy & SymPy Plotting Backends で plot() する際に,思いもかけずにエラーとなった以下のような関数を Maxima でグラフにしたらどうなるか,という比較参考。

関数の定義の中に if 文がある例

例えば,以下のような例。

In [1]:
f(x):= if x < 0 then -x else x;
Out[1]:
\[\tag{${\it \%o}_{1}$}f\left(x\right):=\mathbf{if}\;x<0\;\mathbf{then}\;-x\;\mathbf{else}\;x\]
plot2d()

plot2d() では特に問題なくグラフが作成される。

In [2]:
plot2d(f(x), [x, -10, 10])$

draw2d()

draw2d() でも特に問題なくグラフが作成される。

In [3]:
draw2d(explicit(f(x), x, -10, 10))$

関数の定義の中に数値解法の find_root() がある場合

例えば,以下のような例。

In [4]:
g(x):= find_root(sin(t)-x=0, t, -%pi/2, %pi/2);
Out[4]:
\[\tag{${\it \%o}_{6}$}g\left(x\right):={\it find\_root}\left(\sin t-x=0 , t , \frac{-\pi}{2} , \frac{\pi}{2}\right)\]
plot2d()

plot2d() では,なぜか以下のようなエラーが出てグラフが作成されない。

In [5]:
plot2d(g(x), [x, -1, 1])$
plot2d: expression find_root(sin(t)-x = 0,t,-1.570796326794897,
                             1.570796326794897)

    should  depend only on x, or be an expression of 2 variables
    equal another expression of the same variables.


 -- an error. To debug this try: debugmode(true);
draw2d()

draw2d() では特に問題なくグラフが作成される。

In [6]:
draw2d(explicit(g(x), x, -1, 1))$