1.1.7. 高级表达式操作

参考 Advanced Expression Manipulation

在本节中,我们将讨论一些可以对表达式执行高级操作的方法。

理解表达式树

在我们这样做之前,我们需要了解表达式在 SymPy 中是如何表示的。数学表达式表示为一棵树。让我们采用表达式 \(x^2+xy\),即 x**2 + x*y。我们可以通过使用 srepr 在内部看到这个表达式的样子

import warnings
from sympy import *
import pydot
from IPython.display import SVG

warnings.filterwarnings('ignore')

def display_dot(expr):
    g = pydot.graph_from_dot_data(dotprint(expr))[0]
    g.set_bgcolor('lightyellow')
    t = SVG(g.create_svg())
    return t
x, y, z = symbols('x y z')

expr = x**2 + x*y
srepr(expr)
"Add(Pow(Symbol('x'), Integer(2)), Mul(Symbol('x'), Symbol('y')))"

拆解这个最简单的方法是查看表达式树的图表:

display_dot(expr)
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
~/.local/lib/python3.8/site-packages/pydot.py in create(self, prog, format, encoding)
   1922         try:
-> 1923             stdout_data, stderr_data, process = call_graphviz(
   1924                 program=prog,

~/.local/lib/python3.8/site-packages/pydot.py in call_graphviz(program, arguments, working_dir, **kwargs)
    131 
--> 132     process = subprocess.Popen(
    133         program_with_args,

/usr/lib/python3.8/subprocess.py in __init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, encoding, errors, text)
    853 
--> 854             self._execute_child(args, executable, preexec_fn, close_fds,
    855                                 pass_fds, cwd, env,

/usr/lib/python3.8/subprocess.py in _execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, start_new_session)
   1701                         err_msg = os.strerror(errno_num)
-> 1702                     raise child_exception_type(errno_num, err_msg, err_filename)
   1703                 raise child_exception_type(err_msg)

FileNotFoundError: [Errno 2] No such file or directory: 'dot'

During handling of the above exception, another exception occurred:

FileNotFoundError                         Traceback (most recent call last)
<ipython-input-3-23e704996809> in <module>
----> 1 display_dot(expr)

<ipython-input-1-fa565b96c54e> in display_dot(expr)
      9     g = pydot.graph_from_dot_data(dotprint(expr))[0]
     10     g.set_bgcolor('lightyellow')
---> 11     t = SVG(g.create_svg())
     12     return t

~/.local/lib/python3.8/site-packages/pydot.py in new_method(f, prog, encoding)
   1731                     encoding=None):
   1732                 """Refer to docstring of method `create`."""
-> 1733                 return self.create(
   1734                     format=f, prog=prog, encoding=encoding)
   1735             name = 'create_{fmt}'.format(fmt=frmt)

~/.local/lib/python3.8/site-packages/pydot.py in create(self, prog, format, encoding)
   1931                 args[1] = '"{prog}" not found in path.'.format(
   1932                     prog=prog)
-> 1933                 raise OSError(*args)
   1934             else:
   1935                 raise

FileNotFoundError: [Errno 2] "dot" not found in path.

注解

上图是使用 Graphvizdotprint 函数制作的。

首先,让我们看看这棵树的叶子。符号是类 Symbol 的实例。虽然我们一直在做

x = symbols('x')

我们也可以做到

x = Symbol('x')

无论哪种方式,我们都会得到一个名为“x”的 Symbol。对于表达式中的数字 2,我们得到了 Integer(2)Integer 是整数的 SymPy 类。它类似于 Python 的内置类型 int,不同之处在于 Integer 可以很好地与其他 SymPy 类型一起使用。

当我们写 x**2 时,这会创建一个 Pow 对象。Pow 是“power”的缩写。

srepr(x**2)
"Pow(Symbol('x'), Integer(2))"
display_dot(x**2)
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
~/.local/lib/python3.8/site-packages/pydot.py in create(self, prog, format, encoding)
   1922         try:
-> 1923             stdout_data, stderr_data, process = call_graphviz(
   1924                 program=prog,

~/.local/lib/python3.8/site-packages/pydot.py in call_graphviz(program, arguments, working_dir, **kwargs)
    131 
--> 132     process = subprocess.Popen(
    133         program_with_args,

/usr/lib/python3.8/subprocess.py in __init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, encoding, errors, text)
    853 
--> 854             self._execute_child(args, executable, preexec_fn, close_fds,
    855                                 pass_fds, cwd, env,

/usr/lib/python3.8/subprocess.py in _execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, start_new_session)
   1701                         err_msg = os.strerror(errno_num)
-> 1702                     raise child_exception_type(errno_num, err_msg, err_filename)
   1703                 raise child_exception_type(err_msg)

FileNotFoundError: [Errno 2] No such file or directory: 'dot'

During handling of the above exception, another exception occurred:

FileNotFoundError                         Traceback (most recent call last)
<ipython-input-7-d69e3d2afdc7> in <module>
----> 1 display_dot(x**2)

<ipython-input-1-fa565b96c54e> in display_dot(expr)
      9     g = pydot.graph_from_dot_data(dotprint(expr))[0]
     10     g.set_bgcolor('lightyellow')
---> 11     t = SVG(g.create_svg())
     12     return t

~/.local/lib/python3.8/site-packages/pydot.py in new_method(f, prog, encoding)
   1731                     encoding=None):
   1732                 """Refer to docstring of method `create`."""
-> 1733                 return self.create(
   1734                     format=f, prog=prog, encoding=encoding)
   1735             name = 'create_{fmt}'.format(fmt=frmt)

~/.local/lib/python3.8/site-packages/pydot.py in create(self, prog, format, encoding)
   1931                 args[1] = '"{prog}" not found in path.'.format(
   1932                     prog=prog)
-> 1933                 raise OSError(*args)
   1934             else:
   1935                 raise

FileNotFoundError: [Errno 2] "dot" not found in path.

我们可以通过调用 Pow(x, 2) 创建相同的对象

Pow(x, 2)
\[\displaystyle x^{2}\]

其他的运算以此类推:

expr = sin(x*y)/2 - x**2 + 1/y
expr
\[\displaystyle - x^{2} + \frac{\sin{\left(x y \right)}}{2} + \frac{1}{y}\]
srepr(expr)
"Add(Mul(Integer(-1), Pow(Symbol('x'), Integer(2))), Mul(Rational(1, 2), sin(Mul(Symbol('x'), Symbol('y')))), Pow(Symbol('y'), Integer(-1)))"
display_dot(expr)
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
~/.local/lib/python3.8/site-packages/pydot.py in create(self, prog, format, encoding)
   1922         try:
-> 1923             stdout_data, stderr_data, process = call_graphviz(
   1924                 program=prog,

~/.local/lib/python3.8/site-packages/pydot.py in call_graphviz(program, arguments, working_dir, **kwargs)
    131 
--> 132     process = subprocess.Popen(
    133         program_with_args,

/usr/lib/python3.8/subprocess.py in __init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, encoding, errors, text)
    853 
--> 854             self._execute_child(args, executable, preexec_fn, close_fds,
    855                                 pass_fds, cwd, env,

/usr/lib/python3.8/subprocess.py in _execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, start_new_session)
   1701                         err_msg = os.strerror(errno_num)
-> 1702                     raise child_exception_type(errno_num, err_msg, err_filename)
   1703                 raise child_exception_type(err_msg)

FileNotFoundError: [Errno 2] No such file or directory: 'dot'

During handling of the above exception, another exception occurred:

FileNotFoundError                         Traceback (most recent call last)
<ipython-input-11-23e704996809> in <module>
----> 1 display_dot(expr)

<ipython-input-1-fa565b96c54e> in display_dot(expr)
      9     g = pydot.graph_from_dot_data(dotprint(expr))[0]
     10     g.set_bgcolor('lightyellow')
---> 11     t = SVG(g.create_svg())
     12     return t

~/.local/lib/python3.8/site-packages/pydot.py in new_method(f, prog, encoding)
   1731                     encoding=None):
   1732                 """Refer to docstring of method `create`."""
-> 1733                 return self.create(
   1734                     format=f, prog=prog, encoding=encoding)
   1735             name = 'create_{fmt}'.format(fmt=frmt)

~/.local/lib/python3.8/site-packages/pydot.py in create(self, prog, format, encoding)
   1931                 args[1] = '"{prog}" not found in path.'.format(
   1932                     prog=prog)
-> 1933                 raise OSError(*args)
   1934             else:
   1935                 raise

FileNotFoundError: [Errno 2] "dot" not found in path.