SymPy-1-13-中文文档-十九-

246 阅读1小时+

SymPy 1.13 中文文档(十九)

原文:docs.sympy.org/latest/index.html

偏微分方程

原文:docs.sympy.org/latest/modules/solvers/pde.html

用户函数

这些是导入全局命名空间的函数 from sympy import *。它们供用户使用。

sympy.solvers.pde.pde_separate(eq, fun, sep, strategy='mul')

通过加法或乘法分离方法分离偏微分方程中的变量。它尝试重写方程,使指定的变量之一出现在与其他变量不同的方程一侧。

参数:

  • eq – 偏微分方程

  • fun – 原始函数 F(x, y, z)

  • 分离 – 分离函数列表 [X(x), u(y, z)]

  • 策略 – 分离策略。您可以选择加法分离(‘add’)和乘法分离(‘mul’),默认为乘法分离。

示例

>>> from sympy import E, Eq, Function, pde_separate, Derivative as D
>>> from sympy.abc import x, t
>>> u, X, T = map(Function, 'uXT') 
>>> eq = Eq(D(u(x, t), x), E**(u(x, t))*D(u(x, t), t))
>>> pde_separate(eq, u(x, t), [X(x), T(t)], strategy='add')
[exp(-X(x))*Derivative(X(x), x), exp(T(t))*Derivative(T(t), t)] 
>>> eq = Eq(D(u(x, t), x, 2), D(u(x, t), t, 2))
>>> pde_separate(eq, u(x, t), [X(x), T(t)], strategy='mul')
[Derivative(X(x), (x, 2))/X(x), Derivative(T(t), (t, 2))/T(t)] 

另请参阅

pde_separate_add, pde_separate_mul

sympy.solvers.pde.pde_separate_add(eq, fun, sep)

用于搜索加法可分离解的辅助函数。

考虑具有两个独立变量 x、y 和一个依赖变量 w 的方程式,我们寻找依赖于不同参数的两个函数的乘积:

(w(x, y, z) = X(x) + y(y, z))

示例

>>> from sympy import E, Eq, Function, pde_separate_add, Derivative as D
>>> from sympy.abc import x, t
>>> u, X, T = map(Function, 'uXT') 
>>> eq = Eq(D(u(x, t), x), E**(u(x, t))*D(u(x, t), t))
>>> pde_separate_add(eq, u(x, t), [X(x), T(t)])
[exp(-X(x))*Derivative(X(x), x), exp(T(t))*Derivative(T(t), t)] 
sympy.solvers.pde.pde_separate_mul(eq, fun, sep)

用于搜索乘法可分离解的辅助函数。

考虑具有两个独立变量 x、y 和一个依赖变量 w 的方程式,我们寻找依赖于不同参数的两个函数的乘积:

(w(x, y, z) = X(x)*u(y, z))

示例

>>> from sympy import Function, Eq, pde_separate_mul, Derivative as D
>>> from sympy.abc import x, y
>>> u, X, Y = map(Function, 'uXY') 
>>> eq = Eq(D(u(x, y), x, 2), D(u(x, y), y, 2))
>>> pde_separate_mul(eq, u(x, y), [X(x), Y(y)])
[Derivative(X(x), (x, 2))/X(x), Derivative(Y(y), (y, 2))/Y(y)] 
sympy.solvers.pde.pdsolve(eq, func=None, hint='default', dict=False, solvefun=None, **kwargs)

解决任何(支持的)类型的偏微分方程。

用法

`pdsolve(eq, f(x,y), hint) -> 解决偏微分方程 eq,得到函数 f(x,y),使用方法 hint。

详细信息

eq 可以是任何支持的偏微分方程(请参见

支持的方法的偏微分方程文档字符串)。这可以是一个等式,也可以是一个假设等于 0 的表达式。

f(x,y) 是一个具有该变量导数的两个变量函数。

变量构成偏微分方程。在许多情况下,无需提供它;它将被自动检测到(如果无法检测到,则会引发错误)。

hint 是您希望 pdsolve 使用的解决方法。使用

使用 classify_pde(eq, f(x,y)) 来获取偏微分方程的所有可能提示。默认提示‘default’将使用 classify_pde() 返回的第一个提示。有关可以用作提示的更多选项,请参见下面的提示。

solvefun 是返回任意函数的约定。

由 PDE 求解器。如果用户未设置,它默认为 F。

提示

除了各种解决方法外,还有一些元提示,您可以传递给 pdsolve():

“默认”:

这使用由 classify_pde() 返回的第一个提示。这是 pdsolve() 的默认参数。

“全部”:

要使 pdsolve 应用所有相关的分类提示,请使用 pdsolve(PDE, func, hint=”all”)。这将返回一个提示:解决方案字典条目。如果提示导致 pdsolve 抛出 NotImplementedError,则该提示键的值将是引发的异常对象。该字典还将包括一些特殊键:

  • order: PDE 的阶数。另请参阅 deutils.py 中的 ode_order()。
  • 默认:默认情况下将返回的解决方案。这是由 classify_pde() 返回的元组中首次出现的提示产生的解决方案。

“all_Integral”:

这与“all”相同,只是如果提示还有相应的“_Integral”提示,则仅返回“_Integral”提示。如果“all”由于难以或不可能的积分而导致 pdsolve() 挂起,这将非常有用。这个元提示也比“all”快得多,因为 integrate() 是一个昂贵的例程。

另请参阅 classify_pde() 的文档字符串以获取有关提示的更多信息,以及 pde 的文档字符串以获取所有支持的提示列表。

提示

  • 您可以这样声明未知函数的导数:

    >>> from sympy import Function, Derivative
    >>> from sympy.abc import x, y # x and y are the independent variables
    >>> f = Function("f")(x, y) # f is a function of x and y
    >>> # fx will be the partial derivative of f with respect to x
    >>> fx = Derivative(f, x)
    >>> # fy will be the partial derivative of f with respect to y
    >>> fy = Derivative(f, y) 
    
  • 请查看 test_pde.py 进行许多测试,这也可以作为如何使用 pdsolve() 的示例集。

  • pdsolve 总是返回一个 Equality 类(除非提示为“all”或“all_Integral”)。请注意,无法像 ODE 的情况那样获得 f(x, y) 的显式解。

  • 执行 help(pde.pde_hintname) 以获取有关特定提示的更多信息

示例

>>> from sympy.solvers.pde import pdsolve
>>> from sympy import Function, Eq
>>> from sympy.abc import x, y
>>> f = Function('f')
>>> u = f(x, y)
>>> ux = u.diff(x)
>>> uy = u.diff(y)
>>> eq = Eq(1 + (2*(ux/u)) + (3*(uy/u)), 0)
>>> pdsolve(eq)
Eq(f(x, y), F(3*x - 2*y)*exp(-2*x/13 - 3*y/13)) 
sympy.solvers.pde.classify_pde(eq, func=None, dict=False, *, prep=True, **kwargs)

返回一个 PDE 的可能 pdsolve() 分类的元组。

元组被排序,以便第一项是 pdsolve() 默认用于解决 PDE 的分类。一般来说,列表开头附近的分类比列表末尾附近的分类更快地产生更好的解决方案,尽管总会有例外。要使 pdsolve 使用不同的分类,请使用 pdsolve(PDE, func, hint=)。另请参阅 pdsolve() 文档字符串,了解您可以使用的不同元提示。

如果 dict 为真,则 classify_pde() 将返回一个提示:匹配表达式字典条目。这是为 pdsolve() 的内部使用而设计的。请注意,由于字典的顺序是任意的,因此这很可能不会与元组的顺序相同。

您可以通过执行 help(pde.pde_hintname) 来获取有关不同提示的帮助,其中 hintname 是不带“_Integral”的提示名称。

请查看 sympy.pde.allhints 或 sympy.pde 文档字符串,以获取从 classify_pde 返回的所有支持的提示列表。

示例

>>> from sympy.solvers.pde import classify_pde
>>> from sympy import Function, Eq
>>> from sympy.abc import x, y
>>> f = Function('f')
>>> u = f(x, y)
>>> ux = u.diff(x)
>>> uy = u.diff(y)
>>> eq = Eq(1 + (2*(ux/u)) + (3*(uy/u)), 0)
>>> classify_pde(eq)
('1st_linear_constant_coeff_homogeneous',) 
sympy.solvers.pde.checkpdesol(pde, sol, func=None, solve_for_func=True)

检查给定解是否满足偏微分方程。

pde 是可以以方程或表达式形式给出的偏微分方程。sol 是要检查 pde 是否满足的解。这也可以以方程或表达式形式给出。如果未提供函数,则将使用 deutils 中的辅助函数 _preprocess 来识别函数。

如果传递了一系列解决方案,则将使用相同类型的容器来为每个解决方案返回结果。

目前正在实施以下方法来检查解是否满足 PDE:

  1. 直接将解代入偏微分方程并检查。如果尚未解出(f),则会解出(f),前提是未将 solve_for_func 设置为 False。

如果解满足 PDE,则返回元组(True, 0)。否则返回元组(False, expr),其中 expr 是将解代入 PDE 后获得的值。但如果已知解返回 False,则可能是因为 doit()无法将其简化为零。

举例

>>> from sympy import Function, symbols
>>> from sympy.solvers.pde import checkpdesol, pdsolve
>>> x, y = symbols('x y')
>>> f = Function('f')
>>> eq = 2*f(x,y) + 3*f(x,y).diff(x) + 4*f(x,y).diff(y)
>>> sol = pdsolve(eq)
>>> assert checkpdesol(eq, sol)[0]
>>> eq = x*f(x,y) + f(x,y).diff(x)
>>> checkpdesol(eq, sol)
(False, (x*F(4*x - 3*y) - 6*F(4*x - 3*y)/25 + 4*Subs(Derivative(F(_xi_1), _xi_1), _xi_1, 4*x - 3*y))*exp(-6*x/25 - 8*y/25)) 

提示方法

这些函数用于内部使用。但它们包含有关各种求解方法的有用信息。

sympy.solvers.pde.pde_1st_linear_constant_coeff_homogeneous(eq, func, order, match, solvefun)

解决具有恒定系数的一阶线性齐次偏微分方程。

这个偏微分方程的一般形式是

[a \frac{\partial f(x,y)}{\partial x} + b \frac{\partial f(x,y)}{\partial y} + c f(x,y) = 0]

其中(a)、(b)和(c)是常数。

一般解的形式为:

[f(x, y) = F(- a y + b x ) e^{- \frac{c (a x + b y)}{a² + b²}}]

并且可以在 SymPy 中使用pdsolve找到:

>>> from sympy.solvers import pdsolve
>>> from sympy.abc import x, y, a, b, c
>>> from sympy import Function, pprint
>>> f = Function('f')
>>> u = f(x,y)
>>> ux = u.diff(x)
>>> uy = u.diff(y)
>>> genform = a*ux + b*uy + c*u
>>> pprint(genform)
 d               d
a*--(f(x, y)) + b*--(f(x, y)) + c*f(x, y)
 dx              dy

>>> pprint(pdsolve(genform))
 -c*(a*x + b*y)
 ---------------
 2    2
 a  + b
f(x, y) = F(-a*y + b*x)*e 

举例

>>> from sympy import pdsolve
>>> from sympy import Function, pprint
>>> from sympy.abc import x,y
>>> f = Function('f')
>>> pdsolve(f(x,y) + f(x,y).diff(x) + f(x,y).diff(y))
Eq(f(x, y), F(x - y)*exp(-x/2 - y/2))
>>> pprint(pdsolve(f(x,y) + f(x,y).diff(x) + f(x,y).diff(y)))
 x   y
 - - - -
 2   2
f(x, y) = F(x - y)*e 

参考文献

  • Viktor Grigoryan, “Partial Differential Equations” Math 124A - Fall 2010, pp.7
sympy.solvers.pde.pde_1st_linear_constant_coeff(eq, func, order, match, solvefun)

解决具有恒定系数的一阶线性偏微分方程。

这个偏微分方程的一般形式是

[a \frac{\partial f(x,y)}{\partial x} + b \frac{\partial f(x,y)}{\partial y} + c f(x,y) = G(x,y)]

其中(a)、(b)和(c)是常数,而(G(x, y))可以是(x)和(y)的任意函数。

偏微分方程的一般解为:

[\begin{split}f(x, y) = \left. \left[F(\eta) + \frac{1}{a² + b²} \int\limits^{a x + b y} G\left(\frac{a \xi + b \eta}{a² + b²}, \frac{- a \eta + b \xi}{a² + b²} \right) e^{\frac{c \xi}{a² + b²}}, d\xi\right] e^{- \frac{c \xi}{a² + b²}} \right|_{\substack{\eta=- a y + b x\ \xi=a x + b y }}, ,\end{split}]

其中(F(\eta))是任意单值函数。可以在 SymPy 中使用pdsolve找到解:

>>> from sympy.solvers import pdsolve
>>> from sympy.abc import x, y, a, b, c
>>> from sympy import Function, pprint
>>> f = Function('f')
>>> G = Function('G')
>>> u = f(x, y)
>>> ux = u.diff(x)
>>> uy = u.diff(y)
>>> genform = a*ux + b*uy + c*u - G(x,y)
>>> pprint(genform)
 d               d
a*--(f(x, y)) + b*--(f(x, y)) + c*f(x, y) - G(x, y)
 dx              dy
>>> pprint(pdsolve(genform, hint='1st_linear_constant_coeff_Integral'))
 //          a*x + b*y                                             \         \|
 ||              /                                                 |         ||
 ||             |                                                  |         ||
 ||             |                                      c*xi        |         ||
 ||             |                                     -------      |         ||
 ||             |                                      2    2      |         ||
 ||             |      /a*xi + b*eta  -a*eta + b*xi\  a  + b       |         ||
 ||             |     G|------------, -------------|*e        d(xi)|         ||
 ||             |      |   2    2         2    2   |               |         ||
 ||             |      \  a  + b         a  + b    /               |  -c*xi  ||
 ||             |                                                  |  -------||
 ||            /                                                   |   2    2||
 ||                                                                |  a  + b ||
f(x, y) = ||F(eta) + -------------------------------------------------------|*e       ||
 ||                                  2    2                        |         ||
 \\                                 a  + b                         /         /|eta=-a*y + b*x, xi=a*x + b*y 

举例

>>> from sympy.solvers.pde import pdsolve
>>> from sympy import Function, pprint, exp
>>> from sympy.abc import x,y
>>> f = Function('f')
>>> eq = -2*f(x,y).diff(x) + 4*f(x,y).diff(y) + 5*f(x,y) - exp(x + 3*y)
>>> pdsolve(eq)
Eq(f(x, y), (F(4*x + 2*y)*exp(x/2) + exp(x + 4*y)/15)*exp(-y)) 

参考文献

  • Viktor Grigoryan, “Partial Differential Equations” Math 124A - Fall 2010, pp.7
sympy.solvers.pde.pde_1st_linear_variable_coeff(eq, func, order, match, solvefun)

解决具有可变系数的一阶线性偏微分方程。这个偏微分方程的一般形式是

[a(x, y) \frac{\partial f(x, y)}{\partial x} + b(x, y) \frac{\partial f(x, y)}{\partial y} + c(x, y) f(x, y) = G(x, y)]

其中(a(x, y))、(b(x, y))、(c(x, y))和(G(x, y))是(x)和(y)的任意函数。通过以下变换将这个偏微分方程转换为 ODE:

  1. (\xi)作为(x)

  2. (\eta)作为解的常数,满足偏微分方程(\frac{dy}{dx} = -\frac{b}{a})

进行前述替换后,将其简化为线性 ODE

[a(\xi, \eta)\frac{du}{d\xi} + c(\xi, \eta)u - G(\xi, \eta) = 0]

可以使用dsolve解决。

>>> from sympy.abc import x, y
>>> from sympy import Function, pprint
>>> a, b, c, G, f= [Function(i) for i in ['a', 'b', 'c', 'G', 'f']]
>>> u = f(x,y)
>>> ux = u.diff(x)
>>> uy = u.diff(y)
>>> genform = a(x, y)*u + b(x, y)*ux + c(x, y)*uy - G(x,y)
>>> pprint(genform)
 d                     d
-G(x, y) + a(x, y)*f(x, y) + b(x, y)*--(f(x, y)) + c(x, y)*--(f(x, y))
 dx                    dy 

举例

>>> from sympy.solvers.pde import pdsolve
>>> from sympy import Function, pprint
>>> from sympy.abc import x,y
>>> f = Function('f')
>>> eq =  x*(u.diff(x)) - y*(u.diff(y)) + y**2*u - y**2
>>> pdsolve(eq)
Eq(f(x, y), F(x*y)*exp(y**2/2) + 1) 

参考文献

  • Viktor Grigoryan, “Partial Differential Equations” Math 124A - Fall 2010, pp.7

pde 模块信息

此模块包含 pdsolve()及其使用的不同辅助函数。它受 ode 模块的启发,因此基本架构保持不变。

此模块中的函数

这些是此模块中的用户函数:

  • pdsolve() - 解决 PDE’s
  • classify_pde() - 将偏微分方程分类为 dsolve()可能的提示。
  • pde_separate() - 通过变量分离偏微分方程。
  • 加法或乘法分离方法。

这些是此模块中的辅助函数:

  • pde_separate_add() - 用于搜索加法可分解解的辅助函数。
  • pde_separate_mul() - 用于搜索乘法可分解解的辅助函数。
  • 可分离解。

当前实现的解算器方法

实现了以下方法以解决偏微分方程。有关每个函数的详细信息,请参阅各种 pde_hint()函数的文档字符串(运行 help(pde)):

  • 常系数的一阶线性齐次偏微分方程。
  • 常系数的一阶线性常规偏微分方程。
  • 变系数的一阶线性偏微分方程。

解算器

原文链接:docs.sympy.org/latest/modules/solvers/solvers.html

SymPy 中的solvers模块实现了解方程的方法。

注意

对于专注于解决常见类型方程的初学者指南,请参阅 Solve Equations。

注意

solve()是一个更老、更成熟的通用函数,用于解决许多类型的方程。 solve()具有许多选项,并且在内部使用不同的方法来确定您传递的方程类型,因此如果您知道正在处理的方程类型,可能希望使用更新的solveset()来解决一元方程,linsolve()来解决线性方程组,以及nonlinsolve()来解决非线性方程组。

代数方程

使用solve()来解代数方程。我们假设所有方程都等于 0,因此解决 x**2 == 1 可以转换为以下代码:

>>> from sympy.solvers import solve
>>> from sympy import Symbol
>>> x = Symbol('x')
>>> solve(x**2 - 1, x)
[-1, 1] 

solve()的第一个参数是一个等于零的方程,第二个参数是我们想要解的符号。

sympy.solvers.solvers.solve(f, *symbols, **flags)

代数地解方程和方程组。

参数:

f:

  • 一个单一的 Expr 或 Poly 必须为零
  • 一个等式
  • 一个关系表达式
  • 一个布尔值
  • 以上的一个或多个可迭代对象

符号:(要解决的对象)指定为

  • 未指定(将使用其他非数字对象)
  • 单一符号
  • 符号的去嵌套列表(例如,solve(f, x, y)
  • 符号的有序迭代对象(例如,solve(f, [x, y])

标志:

dict=True(默认为 False)

返回映射解列表(可能为空)。

set=True(默认为 False)

返回符号列表和解的元组集合。

exclude=[](默认)

不要尝试解决排除列表中的任何自由符号;如果给出表达式,其中的自由符号将自动提取。

check=True(默认)

如果为 False,则不进行任何解的测试。如果要包含使任何分母为零的解,这可能很有用。

numerical=True(默认)

如果f仅有一个符号,进行快速数值检查。

minimal=True(默认为 False)

一种非常快速、最小的测试。

warn=True(默认为 False)

如果checksol()无法得出结论,则显示警告。

simplify=True(默认)

在返回它们之前简化所有三阶及以上的多项式,并且(如果检查不为 False)在将它们代入应为零的函数时使用通用简化函数对解决方案和表达式进行简化。

force=True(默认为 False)

使所有没有关于符号的假设的符号为正。

rational=True(默认)

重置浮点数为有理数;如果不使用此选项,包含浮点数的系统可能因多项式问题而无法解决。如果 rational=None,则浮点数将被重置为有理数,但答案将被重置为浮点数。如果标志为 False,则不会对浮点数进行任何处理。

manual=True(默认为 False)

不要使用多项式/矩阵方法来解决方程组,按顺序解决它们,就像“手动”一样。

implicit=True(默认为 False)

允许solve返回模式的解,以其他包含该模式的函数来表示;仅在该模式位于某些可逆函数内部(如 cos,exp 等)时才需要。

particular=True(默认为 False)

指示solve尝试找到线性系统的特定解,其中尽可能多的解为零;这是非常昂贵的。

quick=True(默认为 False;particular必须为 True)

选择一个快速的启发式方法来找到具有许多零的解,而值为 False 则使用保证找到尽可能多零的非常缓慢的方法。

cubics=True(默认)

遇到立方表达式时返回显式解。当为 False 时,四次方和五次方也被禁用。

quartics=True(默认)

遇到四次表达式时返回显式解。当为 False 时,五次方也被禁用。

quintics=True(默认)

遇到五次表达式时返回显式解(如果可能)。

说明

当前支持:

  • 多项式

  • 无穷的

  • 上述内容的分段组合

  • 线性和多项式方程组

  • 包含关系表达式的系统

  • 由未定系数暗示的系统

示例

默认输出根据输入而变化,可能是一个列表(可能为空)、一个字典、一个列表字典或元组,或者是涉及关系的表达式。具体关于可能出现的不同形式的输出,请参见 Solve Output by Type。在这里,可以说为了从solve获取统一的输出,使用dict=Trueset=True(见下文)足够了。

>>> from sympy import solve, Poly, Eq, Matrix, Symbol
>>> from sympy.abc import x, y, z, a, b 

传递的表达式可以是 Expr、Equality 或 Poly 类(或相同类型的列表);矩阵被视为包含矩阵所有元素的列表:

>>> solve(x - 3, x)
[3]
>>> solve(Eq(x, 3), x)
[3]
>>> solve(Poly(x - 3), x)
[3]
>>> solve(Matrix([[x, x + y]]), x, y) == solve([x, x + y], x, y)
True 

如果没有指示感兴趣的符号,并且方程式是单变量的,则返回值列表;否则,字典中的键将指示在表达式中找到的哪些(所有变量中使用的变量和解决方案)变量:

>>> solve(x**2 - 4)
[-2, 2]
>>> solve((x - a)*(y - b))
[{a: x}, {b: y}]
>>> solve([x - 3, y - 1])
{x: 3, y: 1}
>>> solve([x - 3, y**2 - 1])
[{x: 3, y: -1}, {x: 3, y: 1}] 

如果你传递寻找解的符号,输出将根据你传递的符号数量、是否传递表达式列表以及是否解决了线性系统而变化。通过使用dict=Trueset=True来获得统一的输出。

>>> #### *** feel free to skip to the stars below *** ####

>>> from sympy import TableForm

>>> h = [None, ';|;'.join(['e', 's', 'solve(e, s)', 'solve(e, s, dict=True)',

... 'solve(e, s, set=True)']).split(';')]

>>> t = []

>>> for e, s in [

...         (x - y, y),

...         (x - y, [x, y]),

...         (x**2 - y, [x, y]),

...         ([x - 3, y -1], [x, y]),

...         ]:

...     how = [{}, dict(dict=True), dict(set=True)]

...     res = [solve(e, s, **f) for f in how]

...     t.append([e, '|', s, '|'] + [res[0], '|', res[1], '|', res[2]])

...

>>> # ******************************************************* #

>>> TableForm(t, headings=h, alignments="<")

e              | s      | solve(e, s)  | solve(e, s, dict=True) | solve(e, s, set=True)

---------------------------------------------------------------------------------------

x - y          | y      | [x]          | [{y: x}]               | ([y], {(x,)})

x - y          | [x, y] | [(y, y)]     | [{x: y}]               | ([x, y], {(y, y)})

x**2 - y       | [x, y] | [(x, x**2)]  | [{y: x**2}]            | ([x, y], {(x, x**2)})

[x - 3, y - 1] | [x, y] | {x: 3, y: 1} | [{x: 3, y: 1}]         | ([x, y], {(3, 1)}) 
  • 如果任何方程不依赖于给定的符号,则将从方程组中排除它,并且可能以对不感兴趣的变量隐式给出答案:
  • >>> solve([x - y, y - 3], x)
    
    {x: y} 
    

当你传递除一个自由符号外的所有自由符号时,将尝试基于不定系数法找到单个解。如果成功,将返回值的字典。如果要对一个或多个符号的表达式进行代数解,请将其传递给要解决的列表:

>>> e = a*x + b - 2*x - 3
>>> solve(e, [a, b])
{a: 2, b: 3}
>>> solve([e], [a, b])
{a: -b/x + (2*x + 3)/x} 

当没有任何符号的解使得所有表达式都为零时,将返回空列表(或在set=True时返回空集):

>>> from sympy import sqrt
>>> solve(3, x)
[]
>>> solve(x - 3, y)
[]
>>> solve(sqrt(x) + 1, x, set=True)
([x], set()) 

如果作为符号给出的对象不是符号,则会进行代数隔离,并可能获得隐式解。这主要是为了方便你节省将对象替换为符号并解决该符号的时间。它只在指定对象可以使用subs方法替换为符号时才起作用:

>>> from sympy import exp, Function

>>> f = Function('f') 
>>> solve(f(x) - x, f(x))

[x]

>>> solve(f(x).diff(x) - f(x) - x, f(x).diff(x))

[x + f(x)]

>>> solve(f(x).diff(x) - f(x) - x, f(x))

[-x + Derivative(f(x), x)]

>>> solve(x + exp(x)**2, exp(x), set=True)

([exp(x)], {(-sqrt(-x),), (sqrt(-x),)}) 
>>> from sympy import Indexed, IndexedBase, Tuple

>>> A = IndexedBase('A')

>>> eqs = Tuple(A[1] + A[2] - 3, A[1] - A[2] + 1)

>>> solve(eqs, eqs.atoms(Indexed))

{A[1]: 1, A[2]: 2} 
  • 要解决导数中的函数,请使用dsolve()

要隐式解符号,请使用 implicit=True:

>>> solve(x + exp(x), x)
[-LambertW(1)]
>>> solve(x + exp(x), x, implicit=True)
[-exp(x)] 

可以解决表达式中任何可以使用subs替换为符号的内容。

>>> solve(x + 2 + sqrt(3), x + 2)

[-sqrt(3)]

>>> solve((x + 2 + sqrt(3), x + 4 + y), y, x + 2)

{y: -2 + sqrt(3), x + 2: -sqrt(3)} 
  • 在这种隐式求解中没有采取任何英雄主义行为,因此可能最终解中仍然会出现符号:

  • >>> eqs = (x*y + 3*y + sqrt(3), x + 4 + y)
    
    >>> solve(eqs, y, x + 2)
    
    {y: -sqrt(3)/(x + 3), x + 2: -2*x/(x + 3) - 6/(x + 3) + sqrt(3)/(x + 3)}
    
    >>> solve(eqs, y*x, x)
    
    {x: -y - 4, x*y: -3*y - sqrt(3)} 
    
  • 如果尝试解决一个数字,请记住,你获得的数字并不一定意味着该值等同于所获得的表达式:

  • >>> solve(sqrt(2) - 1, 1)
    
    [sqrt(2)]
    
    >>> solve(x - y + 1, 1)  # /!\ -1 is targeted, too
    
    [x/(y - 1)]
    
    >>> [_.subs(z, -1) for _ in solve((x - y + 1).subs(-1, z), 1)]
    
    [-x + y] 
    

附加示例

使用check=True(默认情况下)的solve()将通过符号标签来消除不需要的解。如果不包括任何假设,则将返回所有可能的解:

>>> x = Symbol("x")
>>> solve(x**2 - 1)
[-1, 1] 

通过设置positive标志,将只返回一个解:

>>> pos = Symbol("pos", positive=True)
>>> solve(pos**2 - 1)
[1] 

当检查解时,使得任何分母为零的解将自动被排除。如果不想排除这样的解,请使用check=False选项:

>>> from sympy import sin, limit
>>> solve(sin(x)/x)  # 0 is excluded
[pi] 

如果check=False,则会找到使得分子为零的解,但是当(x = 0)时,(\sin(x)/x)具有众所周知的极限(无间断),为 1:

>>> solve(sin(x)/x, check=False)
[0, pi] 

在下面的情况中,限制存在并等于在check=True时被排除的(x = 0)的值:

>>> eq = x**2*(1/x - z**2/x)
>>> solve(eq, x)
[]
>>> solve(eq, x, check=False)
[0]
>>> limit(eq, x, 0, '-')
0
>>> limit(eq, x, 0, '+')
0 

解决关系

当传递给solve的一个或多个表达式为关系式时,将返回一个关系结果(并且dictset标志将被忽略):

>>> solve(x < 3)
(-oo < x) & (x < 3)
>>> solve([x < 3, x**2 > 4], x)
((-oo < x) & (x < -2)) | ((2 < x) & (x < 3))
>>> solve([x + y - 3, x > 3], x)
(3 < x) & (x < oo) & Eq(x, 3 - y) 

虽然未对关系中的符号进行检查假设,但设置假设将影响某些关系可能自动简化的方式:

>>> solve(x**2 > 4)
((-oo < x) & (x < -2)) | ((2 < x) & (x < oo)) 
>>> r = Symbol('r', real=True)
>>> solve(r**2 > 4)
(2 < r) | (r < -2) 

目前在 SymPy 中没有算法允许您使用关系解析多个变量。因此,以下内容不能确定q < 0(尝试解析rq会引发错误):

>>> from sympy import symbols
>>> r, q = symbols('r, q', real=True)
>>> solve([r + q - 3, r > 3], r)
(3 < r) & Eq(r, 3 - q) 

您可以直接调用solve遇到关系时调用的例程:reduce_inequalities()。它将 Expr 视为 Equality。

>>> from sympy import reduce_inequalities
>>> reduce_inequalities([x**2 - 4])
Eq(x, -2) | Eq(x, 2) 

如果每个关系只包含一个感兴趣的符号,则可以为多个符号处理表达式:

>>> reduce_inequalities([0 <= x  - 1, y < 3], [x, y])
(-oo < y) & (1 <= x) & (x < oo) & (y < 3) 

但是,如果任何关系具有超过一个感兴趣的符号,则会引发错误:

>>> reduce_inequalities([0 <= x*y  - 1, y < 3], [x, y])
Traceback (most recent call last):
...
NotImplementedError:
inequality has more than one symbol of interest. 

禁用高阶显式解决方案

解决多项式表达式时,您可能不希望获得显式解决方案(这可能会很长)。如果表达式是单变量的,则将返回CRootOf实例:

>>> solve(x**3 - x + 1)
[-1/((-1/2 - sqrt(3)*I/2)*(3*sqrt(69)/2 + 27/2)**(1/3)) -
(-1/2 - sqrt(3)*I/2)*(3*sqrt(69)/2 + 27/2)**(1/3)/3,
-(-1/2 + sqrt(3)*I/2)*(3*sqrt(69)/2 + 27/2)**(1/3)/3 -
1/((-1/2 + sqrt(3)*I/2)*(3*sqrt(69)/2 + 27/2)**(1/3)),
-(3*sqrt(69)/2 + 27/2)**(1/3)/3 -
1/(3*sqrt(69)/2 + 27/2)**(1/3)]
>>> solve(x**3 - x + 1, cubics=False)
[CRootOf(x**3 - x + 1, 0),
 CRootOf(x**3 - x + 1, 1),
 CRootOf(x**3 - x + 1, 2)] 

如果表达式是多变量的,则可能不会返回任何解决方案:

>>> solve(x**3 - x + a, x, cubics=False)
[] 

有时即使标志为 False,也会获得解决方案,因为可能对表达式进行了因式分解。在以下示例中,方程可以因式分解为线性因子和二次因子的乘积,因此获得了显式解决方案(这不需要解决三次表达式):

>>> eq = x**3 + 3*x**2 + x - 1
>>> solve(eq, cubics=False)
[-1, -1 + sqrt(2), -sqrt(2) - 1] 

解决涉及根式的方程

由于 SymPy 使用主根的原则,某些根式方程的解将被错过,除非 check=False:

>>> from sympy import root
>>> eq = root(x**3 - 3*x**2, 3) + 1 - x
>>> solve(eq)
[]
>>> solve(eq, check=False)
[1/3] 

在上述示例中,方程只有一个解。其他表达式将产生虚假根,必须手动检查;给奇次幂根提供负参数的根也需要特别检查:

>>> from sympy import real_root, S
>>> eq = root(x, 3) - root(x, 5) + S(1)/7
>>> solve(eq)  # this gives 2 solutions but misses a 3rd
[CRootOf(7*x**5 - 7*x**3 + 1, 1)**15,
CRootOf(7*x**5 - 7*x**3 + 1, 2)**15]
>>> sol = solve(eq, check=False)
>>> [abs(eq.subs(x,i).n(2)) for i in sol]
[0.48, 0.e-110, 0.e-110, 0.052, 0.052] 

第一个解为负数,因此必须使用real_root来查看它是否满足表达式:

>>> abs(real_root(eq.subs(x, sol[0])).n(2))
0.e-110 

如果方程的根不是实数,则需要更多的注意找到根,特别是对于高阶方程。考虑以下表达式:

>>> expr = root(x, 3) - root(x, 5) 

我们将通过选择每个根式的第 1 个根在 x = 3 处构造此表达式的已知值:

>>> expr1 = root(x, 3, 1) - root(x, 5, 1)
>>> v = expr1.subs(x, -3) 

solve函数无法找到此方程的任何精确根:

>>> eq = Eq(expr, v); eq1 = Eq(expr1, v)
>>> solve(eq, check=False), solve(eq1, check=False)
([], []) 

函数unrad可以用于获得方程的一种形式,从而可以找到数值根:

>>> from sympy.solvers.solvers import unrad
>>> from sympy import nroots
>>> e, (p, cov) = unrad(eq)
>>> pvals = nroots(e)
>>> inversion = solve(cov, x)[0]
>>> xvals = [inversion.subs(p, i) for i in pvals] 

尽管eqeq1可以用于查找xvals,但只有通过expr1才能验证解决方案:

>>> z = expr - v
>>> [xi.n(chop=1e-9) for xi in xvals if abs(z.subs(x, xi).n()) < 1e-9]
[]
>>> z1 = expr1 - v
>>> [xi.n(chop=1e-9) for xi in xvals if abs(z1.subs(x, xi).n()) < 1e-9]
[-3.0] 

另请参阅

rsolve

用于解决递归关系

dsolve

用于解决微分方程

sympy.solvers.solvers.solve_linear(lhs, rhs=0, symbols=[], exclude=[])

返回一个从f = lhs - rhs派生的元组,其中一个是以下之一:(0, 1)(0, 0)(symbol, solution)(n, d)

解释

(0, 1)表示fsymbols中不在exclude中的符号无关。

(0, 0)表示在给定的符号中没有方程的解。如果元组的第一个元素不为零,则函数保证依赖于symbols中的符号。

(symbol, solution) 其中符号在 f 的分子中线性出现,在 symbols 中(如果给定),且不在 exclude 中(如果给定)。除了 mul=True 扩展之外,f 不对 f 进行任何简化,因此解决方案将严格对应于唯一解决方案。

(n, d) 其中 ndf 的分子和分母,当分子不是任何感兴趣符号的线性时;除非找到该符号的解决方案(在这种情况下,第二个元素是解决方案,而不是分母)。

示例

>>> from sympy import cancel, Pow 

fsymbols 中不在 exclude 中的符号无关:

>>> from sympy import cos, sin, solve_linear
>>> from sympy.abc import x, y, z
>>> eq = y*cos(x)**2 + y*sin(x)**2 - y  # = y*(1 - 1) = 0
>>> solve_linear(eq)
(0, 1)
>>> eq = cos(x)**2 + sin(x)**2  # = 1
>>> solve_linear(eq)
(0, 1)
>>> solve_linear(x, exclude=[x])
(0, 1) 

变量 x 在以下每个中作为线性变量出现:

>>> solve_linear(x + y**2)
(x, -y**2)
>>> solve_linear(1/x - y**2)
(x, y**(-2)) 

xy 中不是线性时,然后返回分子和分母:

>>> solve_linear(x**2/y**2 - 3)
(x**2 - 3*y**2, y**2) 

如果表达式的分子是一个符号,则如果该符号的解决方案将任何分母设为 0,则返回 (0, 0)

>>> eq = 1/(1/x - 2)
>>> eq.as_numer_denom()
(x, 1 - 2*x)
>>> solve_linear(eq)
(0, 0) 

但是自动重写可能会导致分母中的符号出现在分子中,因此将返回一个解决方案:

>>> (1/x)**-1
x
>>> solve_linear((1/x)**-1)
(x, 0) 

使用未评估的表达式来避免这种情况:

>>> solve_linear(Pow(1/x, -1, evaluate=False))
(0, 0) 

如果允许 x 在以下表达式中取消,则似乎在 x 中是线性的,但 solve_linear 不会执行此类取消,因此解决方案将始终满足原始表达式,而不会引发除零错误。

>>> eq = x**2*(1/x - z**2/x)
>>> solve_linear(cancel(eq))
(x, 0)
>>> solve_linear(eq)
(x**2*(1 - z**2), x) 

可以给出希望解决方案的符号列表:

>>> solve_linear(x + y + z, symbols=[y])
(y, -x - z) 

还可以给出一个要忽略的符号列表:

>>> solve_linear(x + y + z, exclude=[x])
(y, -x - z) 

(因为它是从符号的规范排序列表中第一个具有线性解的变量,所以得到了 y 的解。)

sympy.solvers.solvers.solve_linear_system(system, *symbols, **flags)

解决具有 (N) 个线性方程和 (M) 个变量的系统,这意味着支持欠定和过度定系统。

解释

可能的解数是零、一或无限。相应地,此过程将返回 None 或一个具有解的字典。在欠定系统的情况下,所有任意参数都将被跳过。这可能导致返回一个空字典的情况。在这种情况下,可以为所有符号分配任意值。

此函数的输入是一个 (N\times M + 1) 矩阵,这意味着它必须是增广形式。如果您喜欢输入 (N) 个方程和 (M) 个未知数,那么请使用 solve(Neqs, *Msymbols)。注意:此例程会制作矩阵的本地副本,因此传递的矩阵不会被修改。

这里使用的算法是无分数高斯消元法,在消元后得到一个上三角矩阵。然后使用回代法找到解。这种方法比高斯-约旦方法更有效和更紧凑。

示例

>>> from sympy import Matrix, solve_linear_system
>>> from sympy.abc import x, y 

解决以下系统:

 x + 4 y ==  2
-2 x +   y == 14 
>>> system = Matrix(( (1, 4, 2), (-2, 1, 14)))
>>> solve_linear_system(system, x, y)
{x: -6, y: 2} 

一个退化系统返回一个空字典:

>>> system = Matrix(( (0,0,0), (0,0,0) ))
>>> solve_linear_system(system, x, y)
{} 
sympy.solvers.solvers.solve_linear_system_LU(matrix, syms)

使用 LUsolve 解决增广矩阵系统,并返回一个字典,其中解按顺序键入为 syms 的符号。

解释

矩阵必须可逆。

示例

>>> from sympy import Matrix, solve_linear_system_LU
>>> from sympy.abc import x, y, z 
>>> solve_linear_system_LU(Matrix([
... [1, 2, 0, 1],
... [3, 2, 2, 1],
... [2, 0, 0, 1]]), [x, y, z])
{x: 1/2, y: 1/4, z: -1/2} 

另请参阅

LUsolve

sympy.solvers.solvers.solve_undetermined_coeffs(equ, coeffs, *syms, **flags)

解决一个由匹配coeffs变量中的系数形成的包含(k)个参数的方程组,这些系数依赖于其余变量(或由syms显式给出)。

解释

此函数的结果是一个字典,其中包含关于(q)中系数的符号值 - 如果没有解或系数不出现在方程中,则为空 - 否则为 None(如果系统未被识别)。如果存在多个解,解将作为列表传递。输出可以使用与solve相同的语义进行修改,因为传递的标志直接发送到solve,例如标志dict=True将始终返回作为字典的解的列表。

此函数接受 Equality 和 Expr 类实例。当指定符号以确定要确定的参数时,解决过程最有效,但将尝试确定它们(如果不存在)。如果未获得预期的解决方案(并且未指定符号),请尝试指定它们。

示例

>>> from sympy import Eq, solve_undetermined_coeffs
>>> from sympy.abc import a, b, c, h, p, k, x, y 
>>> solve_undetermined_coeffs(Eq(a*x + a + b, x/2), [a, b], x)
{a: 1/2, b: -1/2}
>>> solve_undetermined_coeffs(a - 2, [a])
{a: 2} 

方程可以在符号中是非线性的:

>>> X, Y, Z = y, x**y, y*x**y
>>> eq = a*X + b*Y + c*Z - X - 2*Y - 3*Z
>>> coeffs = a, b, c
>>> syms = x, y
>>> solve_undetermined_coeffs(eq, coeffs, syms)
{a: 1, b: 2, c: 3} 

系数在非线性的情况下也可以是非线性的,但如果只有一个解,则将其作为字典返回:

>>> eq = a*x**2 + b*x + c - ((x - h)**2 + 4*p*k)/4/p
>>> solve_undetermined_coeffs(eq, (h, p, k), x)
{h: -b/(2*a), k: (4*a*c - b**2)/(4*a), p: 1/(4*a)} 

多个解始终作为列表返回:

>>> solve_undetermined_coeffs(a**2*x + b - x, [a, b], x)
[{a: -1, b: 0}, {a: 1, b: 0}] 

使用标志dict=True(符合solve()中的语义)将强制结果始终为包含任何解的列表。

>>> solve_undetermined_coeffs(a*x - 2*x, [a], dict=True)
[{a: 2}] 
sympy.solvers.solvers.nsolve(*args, dict=False, **kwargs)

数值解非线性方程组:nsolve(f, [args,] x0, modules=['mpmath'], **kwargs)

解释

f是表示系统的符号表达式的向量函数。args是变量。如果只有一个变量,则可以省略此参数。x0是接近解的起始向量。

使用 modules 关键字指定应用于评估函数和雅可比矩阵的模块。确保使用支持矩阵的模块。有关语法的更多信息,请参阅lambdify的文档字符串。

如果关键字参数包含dict=True(默认为 False),nsolve将返回一个解映射的列表(可能为空)。如果想要使用nsolve作为solve的替代解决方案,这可能特别有用,因为对于两种方法都使用 dict 参数会产生一致类型结构的返回值。请注意:为了保持与solve的一致性,即使nsolve(目前至少)一次只找到一个解,解也将作为列表返回。

支持超定系统。

示例

>>> from sympy import Symbol, nsolve
>>> import mpmath
>>> mpmath.mp.dps = 15
>>> x1 = Symbol('x1')
>>> x2 = Symbol('x2')
>>> f1 = 3 * x1**2 - 2 * x2**2 - 1
>>> f2 = x1**2 - 2 * x1 + x2**2 + 2 * x2 - 8
>>> print(nsolve((f1, f2), (x1, x2), (-1, 1)))
Matrix([[-1.19287309935246], [1.27844411169911]]) 

对于一维函数,语法更简单:

>>> from sympy import sin, nsolve
>>> from sympy.abc import x
>>> nsolve(sin(x), x, 2)
3.14159265358979
>>> nsolve(sin(x), 2)
3.14159265358979 

要以高于默认精度解决,请使用 prec 参数:

>>> from sympy import cos
>>> nsolve(cos(x) - x, 1)
0.739085133215161
>>> nsolve(cos(x) - x, 1, prec=50)
0.73908513321516064165531208767387340401341175890076
>>> cos(_)
0.73908513321516064165531208767387340401341175890076 

要解决实函数的复根,必须指定非实数的初始点:

>>> from sympy import I
>>> nsolve(x**2 + 2, I)
1.4142135623731*I 

使用mpmath.findroot,您可以找到更详细的文档,特别是关于关键字参数和可用求解器的部分。但请注意,对于在根附近非常陡峭的函数,验证解可能会失败。在这种情况下,您应该使用标志verify=False并独立验证解决方案。

>>> from sympy import cos, cosh
>>> f = cos(x)*cosh(x) - 1
>>> nsolve(f, 3.14*100)
Traceback (most recent call last):
...
ValueError: Could not find root within given tolerance. (1.39267e+230 > 2.1684e-19)
>>> ans = nsolve(f, 3.14*100, verify=False); ans
312.588469032184
>>> f.subs(x, ans).n(2)
2.1e+121
>>> (f/f.diff(x)).subs(x, ans).n(2)
7.4e-15 

如果根的边界已知且使用二分法,可以安全地跳过验证:

>>> bounds = lambda i: (3.14*i, 3.14*(i + 1))
>>> nsolve(f, bounds(100), solver='bisect', verify=False)
315.730061685774 

或者,当忽略分母时,函数可能行为更佳。然而,并非总是如此,因此使用哪个函数的决定留给用户决定。

>>> eq = x**2/(1 - x)/(1 - 2*x)**2 - 100
>>> nsolve(eq, 0.46)
Traceback (most recent call last):
...
ValueError: Could not find root within given tolerance. (10000 > 2.1684e-19)
Try another starting point or tweak arguments.
>>> nsolve(eq.as_numer_denom()[0], 0.46)
0.46792545969349058 
sympy.solvers.solvers.checksol(f, symbol, sol=None, **flags)

检查 sol 是否为方程f == 0的解。

解释

输入可以是单个符号及其对应值,也可以是符号和值的字典。当作为字典给出且标志simplify=True时,字典中的值将被简化。 f 可以是单个方程或方程的可迭代对象。解必须满足f中的所有方程才被认为是有效的;如果一个解不满足任何方程,则返回 False;如果一个或多个检查无法得出结论(且没有 False),则返回 None。

示例

>>> from sympy import checksol, symbols
>>> x, y = symbols('x,y')
>>> checksol(x**4 - 1, x, 1)
True
>>> checksol(x**4 - 1, x, 0)
False
>>> checksol(x**2 + y**2 - 5**2, {x: 3, y: 4})
True 

要使用checksol()检查表达式是否为零,将其作为f传递,并为symbol发送空字典:

>>> checksol(x**2 + x - x*(x + 1), {})
True 

如果checksol()无法得出结论,则返回 None。

标志:

‘numerical=True(默认)’

如果f只有一个符号,则进行快速数值检查。

‘minimal=True(默认为 False)’

一个非常快速的、最小化的测试。

‘warn=True(默认为 False)’

如果checksol()无法得出结论,则显示警告。

‘simplify=True(默认)’

在将解代入函数之前简化解,以及在尝试特定简化之前简化函数

‘force=True(默认为 False)’

使所有符号为正,不假设符号的符号。

sympy.solvers.solvers.unrad(eq, *syms, **flags)

去除具有符号参数的根并返回(eq, cov),None 或引发错误。

解释

如果没有根可去除,则返回 None。

如果有根并且无法去除或者无法解决重写系统为多项式所需的原始符号与变量变化关系,则引发 NotImplementedError。

否则返回元组(eq, cov),其中:

eq, cov

eq 是一个没有根的方程(在感兴趣的符号中),其解集是原始表达式的超集。 eq 可以用新变量重新表达;与原始变量的关系由cov给出,其中包含vv**p - b的列表,其中p是清除根所需的幂,b是现在用感兴趣的符号表示的根的多项式。例如,对于 sqrt(2 - x),元组将是(c, c**2 - 2 + x)eq 的解将包含原方程的解(如果有的话)。

syms

如果提供了一个符号的可迭代对象,则限制根式的集中焦点:只有带有一个或多个感兴趣符号的根式将被清除。如果未设置syms,则使用所有自由符号。

flags在递归调用期间用于内部通信。还识别两个选项:

take,如果定义了,则被解释为一个单参数函数,如果给定的 Pow 应该处理,则返回 True。

如果表达式中有根式,可以将其去除:

  • 所有根的底数都相同;在这种情况下进行变量更改。
  • 如果表达式中所有根都出现在一个项中。
  • 只有四个带有 sqrt()因子的项,或者少于四个具有 sqrt()因子的项。
  • 只有两个带有根式的项。

示例

>>> from sympy.solvers.solvers import unrad
>>> from sympy.abc import x
>>> from sympy import sqrt, Rational, root 
>>> unrad(sqrt(x)*x**Rational(1, 3) + 2)
(x**5 - 64, [])
>>> unrad(sqrt(x) + root(x + 1, 3))
(-x**3 + x**2 + 2*x + 1, [])
>>> eq = sqrt(x) + root(x, 3) - 2
>>> unrad(eq)
(_p**3 + _p**2 - 2, [_p, _p**6 - x]) 

普通微分方程(ODEs)

参见 ODE。

偏微分方程(PDEs)

参见 PDE。

Deutils(用于解决 ODE 和 PDE 的实用程序)

sympy.solvers.deutils.ode_order(expr, func)

返回给定微分方程关于 func 的阶数。

此函数采用递归实现。

示例

>>> from sympy import Function
>>> from sympy.solvers.deutils import ode_order
>>> from sympy.abc import x
>>> f, g = map(Function, ['f', 'g'])
>>> ode_order(f(x).diff(x, 2) + f(x).diff(x)**2 +
... f(x).diff(x), f(x))
2
>>> ode_order(f(x).diff(x, 2) + g(x).diff(x, 3), f(x))
2
>>> ode_order(f(x).diff(x, 2) + g(x).diff(x, 3), g(x))
3 

递归方程

sympy.solvers.recurr.rsolve(f, y, init=None)

解决具有有理系数的一元递归。

给定(k)阶线性递归(\operatorname{L} y = f),或等效地:

[a_{k}(n) y(n+k) + a_{k-1}(n) y(n+k-1) + \cdots + a_{0}(n) y(n) = f(n)]

其中(a_{i}(n)),对于(i=0, \ldots, k),是关于(n)的多项式或有理函数,而(f)是超几何函数或在(n)上具有固定数量不同超几何项的和,找到所有解或返回None,如果没有找到。

初始条件可以作为字典的两种形式给出:

  1. {  n_0  : v_0,   n_1  : v_1, ...,   n_m  : v_m}
  2. {y(n_0) : v_0, y(n_1) : v_1, ..., y(n_m) : v_m}

或作为值列表L

L = [v_0, v_1, ..., v_m]

其中L[i] = v_i,对于(i=0, \ldots, m),映射到(y(n_i))。

示例

让我们考虑以下递归:

[(n - 1) y(n + 2) - (n² + 3 n - 2) y(n + 1) + 2 n (n + 1) y(n) = 0]

>>> from sympy import Function, rsolve
>>> from sympy.abc import n
>>> y = Function('y') 
>>> f = (n - 1)*y(n + 2) - (n**2 + 3*n - 2)*y(n + 1) + 2*n*(n + 1)*y(n) 
>>> rsolve(f, y(n))
2**n*C0 + C1*factorial(n) 
>>> rsolve(f, y(n), {y(0):0, y(1):3})
3*2**n - 3*factorial(n) 

参见

rsolve_poly, rsolve_ratio, rsolve_hyper

sympy.solvers.recurr.rsolve_poly(coeffs, f, n, shift=0, **hints)

给定具有多项式系数和非齐次方程(\operatorname{L} y = f)的(k)阶线性递归运算符(\operatorname{L}),其中(f)是一个多项式,在特征为零的域(K)上寻找所有多项式解。

算法执行两个基本步骤:

  1. 计算一般多项式解的度(N)。
  2. 找到所有(N)次或更低次的多项式(\operatorname{L} y = f)。

有两种方法来计算多项式解。如果度约束相对较小,即小于或等于递归的阶数,则使用未知系数法。这将得到一个带有(N+1)未知数的代数方程组。

另一种情况下,算法将初始方程转化为等价的方程,使得代数方程组仅有(r)个不定元。这种方法相对于朴素方法更为复杂,并由 Abramov、Bronstein 和 Petkovsek 共同发明。

可以将此处实现的算法推广到线性(q)-差分和微分方程的情况。

假设我们想计算到常数的第(m)个伯努利多项式。为此,我们可以使用(b(n+1) - b(n) = m n^{m-1})的递推关系,其解为(b(n) = B_m + C)。例如:

>>> from sympy import Symbol, rsolve_poly
>>> n = Symbol('n', integer=True) 
>>> rsolve_poly([-1, 1], 4*n**3, n)
C0 + n**4 - 2*n**3 + n**2 

参考文献

[R890]

S. A. Abramov, M. Bronstein 和 M. Petkovsek,关于线性算子方程的多项式解,见:T. Levelt,编,Proc. ISSAC ‘95,ACM Press,New York,1995,290-296。

[R891]

M. Petkovsek,具有多项式系数的线性递推的超几何解,J. Symbolic Computation,14 (1992),243-264。

[R892]

  1. Petkovsek, H. S. Wilf, D. Zeilberger,A = B,1996。
sympy.solvers.recurr.rsolve_ratio(coeffs, f, n, **hints)

给定具有多项式系数的阶为(k)的线性递推算子(\operatorname{L})和非齐次方程(\operatorname{L} y = f),其中(f)是多项式,我们寻求特征零域(K)上所有有理解。

此过程仅接受多项式,但如果您有兴趣解决有理系数的递推,则使用rsolve,它将预处理给定的方程并使用多项式参数运行此过程。

此算法执行两个基本步骤:

  1. 计算可以作为方程(\operatorname{L} y = f)任何有理解的通用分母的多项式(v(n))。
  2. 通过替换(y(n) = u(n)/v(n))构造新的线性差分方程,并解出(u(n))找到其所有的多项式解。如果找不到任何解,则返回None

此处实现的算法是原始 Abramov 算法的修订版,于 1989 年开发。新方法实现更简单,整体效率更佳。此方法可以轻松地适应(q)-差分方程的情况。

除了单独找到有理解外,此函数还是 Hyper 算法的重要部分,用于寻找递推的非齐次部分的特解。

示例

>>> from sympy.abc import x
>>> from sympy.solvers.recurr import rsolve_ratio
>>> rsolve_ratio([-2*x**3 + x**2 + 2*x - 1, 2*x**3 + x**2 - 6*x,
... - 2*x**3 - 11*x**2 - 18*x - 9, 2*x**3 + 13*x**2 + 22*x + 8], 0, x)
C0*(2*x - 3)/(2*(x**2 - 1)) 

参见

rsolve_hyper

参考文献

[R893]

S. A. Abramov,具有多项式系数的线性差分和(q)-差分方程的有理解,见:T. Levelt,编,Proc. ISSAC ‘95,ACM Press,New York,1995,285-289。

sympy.solvers.recurr.rsolve_hyper(coeffs, f, n, **hints)

给定具有多项式系数的阶为(k)的线性递推算子(\operatorname{L})和非齐次方程(\operatorname{L} y = f),我们寻求所有特征零域(K)上的超几何解。

不齐次部分可以是超几何的,也可以是一组固定数量的两两不相似超几何项的总和。

该算法执行三个基本步骤:

  1. 将 (\operatorname{L} y = f) 不齐次部分中类似的超几何项分组,并使用 Abramov 算法找到特解。
  2. 计算 (\operatorname{L}) 的生成集并找到其基础,以便所有解线性无关。
  3. 用与 (\operatorname{L}) 基础维数相等的任意常数形成最终解。

如果 a(n) 由具有多项式系数的一阶线性差分方程消灭,或者更简单地说,如果连续项比是有理函数,则其为超几何项。

此过程的输出是固定数量的超几何项的线性组合。但是,底层方法可以生成更大类别的解 - D’Alembert 项。

注意,此方法不仅计算不齐次方程的核,还将其缩减为基础,以便通过此过程生成的解决方案是线性无关的。

示例

>>> from sympy.solvers import rsolve_hyper
>>> from sympy.abc import x 
>>> rsolve_hyper([-1, -1, 1], 0, x)
C0*(1/2 - sqrt(5)/2)**x + C1*(1/2 + sqrt(5)/2)**x 
>>> rsolve_hyper([-1, 1], 1 + x, x)
C0 + x*(x + 1)/2 

参考文献

[R894]

M. Petkovsek,带有多项式系数的线性递推超几何解,J. Symbolic Computation,14 (1992),243-264。

[R895]

  1. Petkovsek,H. S. Wilf,D. Zeilberger,A = B,1996。

多项式方程组

sympy.solvers.polysys.solve_poly_system(seq, *gens, strict=False, **args)

返回多项式方程组的解列表,否则返回 None。

参数:

seq: 列表/元组/集合

列出需要解决的所有方程

gens: 生成器

seq 的方程的生成器,我们希望得到解的生成器

strict: 布尔值(默认为 False)

如果 strict 为 True,则在已知解决方案可能不完整时会引发 NotImplementedError(这可能发生在不能用根式表示所有解的情况下)

args: 关键字参数

解方程的特殊选项。

返回:

List[Tuple]

元组列表,其中元素为按 gens 传递顺序解决方案的符号

当计算的基础仅包含地面时返回 None。

示例

>>> from sympy import solve_poly_system
>>> from sympy.abc import x, y 
>>> solve_poly_system([x*y - 2*y, 2*y**2 - x**2], x, y)
[(0, 0), (2, -sqrt(2)), (2, sqrt(2))] 
>>> solve_poly_system([x**5 - x + y**3, y**2 - 1], x, y, strict=True)
Traceback (most recent call last):
...
UnsolvableFactorError 
sympy.solvers.polysys.solve_triangulated(polys, *gens, **args)

使用 Gianni-Kalkbrenner 算法解多项式系统。

该算法通过在地面域中计算一个 Groebner 基础,然后通过在地面域的适当构造的代数扩展中迭代地计算多项式因式来进行。

参数:

polys: 列表/元组/集合

列出需要解决的所有方程

gens: 生成器

polys 中我们想要解的方程的生成器

args: 关键字参数

解方程的特殊选项

返回:

List[Tuple]

元组列表。满足 polys 中列出方程的符号的解决方案

示例

>>> from sympy import solve_triangulated
>>> from sympy.abc import x, y, z 
>>> F = [x**2 + y + z - 1, x + y**2 + z - 1, x + y + z**2 - 1] 
>>> solve_triangulated(F, x, y, z)
[(0, 0, 1), (0, 1, 0), (1, 0, 0)] 

参考文献

1. Patrizia Gianni,Teo Mora,使用 Groebner 基础代数解多项式方程组,应用代数,代数算法和纠错编码的 AAECC-5,LNCS 356 247–257,1989

丢番图方程(DEs)

参见丢番图方程

不等式

参见不等式求解器

线性规划(优化)

sympy.solvers.simplex.lpmax(f, constr)

返回线性方程f在使用 Ge、Le 或 Eq 表达的线性约束下的最大值。

所有变量都是未约束的,除非受到约束。

示例

>>> from sympy.solvers.simplex import lpmax
>>> from sympy import Eq
>>> from sympy.abc import x, y
>>> lpmax(x, [2*x - 3*y >= -1, Eq(x+ 3*y,2), x <= 2*y])
(4/5, {x: 4/5, y: 2/5}) 

变量的负值是允许的,除非明确排除:

>>> lpmax(x, [x <= -1])
(-1, {x: -1}) 

如果为x添加了非负约束,则没有可能的解决方案:

>>> lpmax(x, [x <= -1, x >= 0])
Traceback (most recent call last):
...
sympy.solvers.simplex.InfeasibleLPError: inconsistent/False constraint 

参见

linprog, lpmin

sympy.solvers.simplex.lpmin(f, constr)

返回在使用 Ge、Le 或 Eq 表达的线性约束下的线性方程f的最小值。

所有变量都是未约束的,除非受到约束。

示例

>>> from sympy.solvers.simplex import lpmin
>>> from sympy import Eq
>>> from sympy.abc import x, y
>>> lpmin(x, [2*x - 3*y >= -1, Eq(x + 3*y, 2), x <= 2*y])
(1/3, {x: 1/3, y: 5/9}) 

变量的负值是允许的,除非明确排除,因此最小化x对于x <= 3是一个无约束问题,而以下问题有一个有界解:

>>> lpmin(x, [x >= 0, x <= 3])
(0, {x: 0}) 

如果没有指明x是非负的,这个目标没有最小值:

>>> lpmin(x, [x <= 3])
Traceback (most recent call last):
...
sympy.solvers.simplex.UnboundedLPError:
Objective function can assume arbitrarily large values! 

参见

linprog, lpmax

sympy.solvers.simplex.linprog(c, A=None, b=None, A_eq=None, b_eq=None, bounds=None)

返回在给定约束A*x <= bA_eq*x = b_eqc*x的最小化值。除非给出边界,否则变量在解中将具有非负值。

如果没有给出A,那么系统的维度将由C的长度确定。

默认情况下,所有变量都将是非负的。如果bounds作为单个元组(lo, hi)给出,则所有变量将被限制在lohi之间。使用 None 表示lohi在负或正方向上没有约束,例如(None, 0)表示非正值。要设置单个范围,传递一个长度等于A列数的列表,每个元素都是一个元组;如果只有少数变量取非默认值,则可以作为字典传递,键给出相应分配变量的列,例如bounds={2: (1, 4)}将限制第三个变量的值在[1, 4]范围内。

示例

>>> from sympy.solvers.simplex import linprog
>>> from sympy import symbols, Eq, linear_eq_to_matrix as M, Matrix
>>> x = x1, x2, x3, x4 = symbols('x1:5')
>>> X = Matrix(x)
>>> c, d = M(5*x2 + x3 + 4*x4 - x1, x)
>>> a, b = M([5*x2 + 2*x3 + 5*x4 - (x1 + 5)], x)
>>> aeq, beq = M([Eq(3*x2 + x4, 2), Eq(-x1 + x3 + 2*x4, 1)], x)
>>> constr = [i <= j for i,j in zip(a*X, b)]
>>> constr += [Eq(i, j) for i,j in zip(aeq*X, beq)]
>>> linprog(c, a, b, aeq, beq)
(9/2, [0, 1/2, 0, 1/2])
>>> assert all(i.subs(dict(zip(x, _[1]))) for i in constr) 

参见

lpmin, lpmax

Solveset

原文链接:docs.sympy.org/latest/modules/solvers/solveset.html

这是solverssolveset模块的官方文档。它包含关于我们解方程新模块的常见问题。

注意

想要一个适合初学者的指南,专注于解决常见类型的方程,请参阅 Solve Equations。

solve() 有什么问题:

SymPy 已经有一个相当强大的 solve 函数。但是它有一些缺陷。例如:

  1. 它没有一致的输出类型来处理各种类型的解决方案。它需要始终返回很多类型的解决方案:

    • 单一解:(x = 1)

    • 多解方程:(x² = 1)

    • 无解:(x² + 1 = 0 ; x \in \mathbb{R})

    • 解的区间:(\lfloor x \rfloor = 0)

    • 无限多解:(\sin(x) = 0)

    • 多元函数有点解:(x² + y² = 0)

    • 多元函数有非点解:(x² + y² = 1)

    • 方程组:(x + y = 1) 和 (x - y = 0)

    • 关系表达式:(x > 0)

    • 而且最重要的案例是:“我们不知道”

  2. 输入 API 有很多参数,使用起来可能会有困难。

  3. 有时候在使用临界点找出函数的极大值和极小值时,知道它是否返回了所有的解决方案是很重要的。solve不能保证这一点。

为什么要使用 Solveset?

  • solveset 有一个备选的一致的输入和输出接口:solveset 返回一个集合对象,而集合对象负责处理所有类型的输出。对于它不“知道”所有解决方案的情况,会返回一个具有部分解决方案的 ConditionSet。对于输入,它只接受方程、要解的变量和可选的参数 domain,这个参数表示解方程的定义域。

  • solveset 可以返回无限多个解。例如,求解 (\sin{(x)} = 0) 会返回 ({2 n \pi | n \in \mathbb{Z}} \cup {2 n \pi + \pi | n \in \mathbb{Z}}),而 solve 只返回区间 ([0, \pi])。

  • 在复数域和实数域中,对方程求解器在代码级别和接口级别上有明确的分离。例如,在复数域中解 (e^x = 1),返回所有解的集合,即 ({2 n i \pi | n \in \mathbb{Z}}),而在实数域中,仅返回 ({0})。

为什么我们要将集合作为输出类型使用?

SymPy 有一个完善的集合模块,可以表示数学中大多数集合容器,比如:

  • FiniteSet

  • 表示离散数字的有限集。

  • Interval

  • 表示实数区间作为一个集合。

  • ProductSet

  • 表示集合的笛卡尔积。

  • ImageSet

  • 表示数学函数下的集合的像

    >>> from sympy import ImageSet, S, Lambda
    
    >>> from sympy.abc import x
    
    >>> squares = ImageSet(Lambda(x, x**2), S.Naturals)  # {x**2 for x in N}
    
    >>> 4 in squares
    
    True 
    
  • ComplexRegion

  • 表示在阿格朗平面的区域中所有复数的集合。

  • ConditionSet

  • 表示满足给定条件的元素集合。

此外,还有预定义的集合类,如:

  • Naturals, (\mathbb{N})

  • 表示自然数(或计数数),即从 1 开始的所有正整数。

  • Naturals0, (\mathbb{N_0})

  • 表示整数,即所有非负整数,包括 0。

  • Integers, (\mathbb{Z})

  • 表示所有整数:正数、负数和零。

  • Reals, (\mathbb{R})

  • 表示所有实数的集合。

  • Complexes, (\mathbb{C})

  • 表示所有复数的集合。

  • EmptySet, (\emptyset)

  • 表示空集。

上述六个集合都可以作为单例集合,如 S.Integers

它能够执行大多数数学中的集合运算:

  • Union
  • Intersection
  • Complement
  • SymmetricDifference

使用集合作为解算器的输出的主要原因是它可以一致地表示许多类型的解。对于单变量情况,它可以表示:

  • 无解(由空集)。
  • 有限多的解(通过 FiniteSet)。
  • 无限多的解,包括可数和不可数的无限多解(使用 ImageSet 模块)。
  • Interval
  • 方程的解集也可能是奇怪的,如有理数的集合。

没有其他 Python 对象(列表、字典、生成器、Python 集合)能够提供我们集合模块试图模拟的数学集的灵活性。使用集合的第二个原因是它们接近数学家处理的实体,这样更容易对它们进行推理。当可能时,集合对象遵循 Pythonic 约定,即 x in Afor i in A 都在可以计算时起作用。使用更接近数学实体的对象的另一个优势是用户不必“学习”我们的表示,她可以从她的数学经验中传递她的期望。

对于多变量情况,我们将解表示为 n 维空间中的一组点集,点由有序元组的 FiniteSet 表示,这是 (\mathbb{R}^n) 或 (\mathbb{C}^n) 中的点。

请注意,一般的 FiniteSet 是无序的,但带有元组作为其唯一参数的 FiniteSet 是有序的,因为元组是有序的。因此,元组中的顺序被映射到变量的预定义顺序,而返回解决方案。

例如:

>>> from sympy import FiniteSet
>>> FiniteSet(1, 2, 3)   # Unordered
{1, 2, 3}
>>> FiniteSet((1, 2, 3))  # Ordered
{(1, 2, 3)} 

为什么不使用字典作为输出?

在程序上处理字典很容易,但在数学上它们并不是非常精确的,使用它们很快会导致不一致和混乱。例如:

  • 存在许多情况我们不知道完整的解决方案,我们可能希望输出一个部分解决方案,考虑方程 (fg = 0)。这个方程的解是以下两个方程的解的并集:(f = 0),(g = 0)。假设我们能解决 (f = 0),但尚不支持解决 (g = 0)。在这种情况下,我们不能使用字典表示给定方程 (fg = 0) 的部分解决方案。使用 ConditionSet 对象解决了这个问题。

  • (\left{sol_f \cup {x | x ∊ \mathbb{R} ∧ g = 0}\right}),其中 (\left{sol_f\right}) 是方程 (f = 0) 的解。

  • 使用字典可能会导致意外的结果,比如:

    • solve(Eq(x**2, 1), x) != solve(Eq(y**2, 1), y)
    • 在数学上,这是没有意义的。在这里使用 FiniteSet 解决了这个问题。
  • 它也不能表示像 (|x| < 1) 这样的方程的解,这在阿尔干平面中是半径为 1 的圆盘。使用 ComplexRegion 实现解决了这个问题。

solveset 的输入 API

solveset 具有更简单的输入 API,不像 solve。它最多接受三个参数:

solveset(equation, variable=None, domain=S.Complexes)

方程

要解决的方程。

变量

要解决的方程的变量。

方程的解决域。

solveset 移除了 solveflags 参数,这使得输入 API 更加复杂且输出 API 不一致。

这个域参数是什么?

Solveset 设计为与被解决的变量的假设无关,而是使用 domain 参数来决定分派给解算器的方程,即 solveset_realsolveset_complex。这与旧的 solve 不同,后者考虑变量的假设。

>>> from sympy import solveset, S
>>> from sympy.abc import x
>>> solveset(x**2 + 1, x) # domain=S.Complexes is default
{-I, I}
>>> solveset(x**2 + 1, x, domain=S.Reals)
EmptySet 

解方程所采用的一般方法是什么?

Solveset 使用各种方法来解决方程,以下是方法论的简要概述:

  • 首先考虑 domain 参数,以确定用户感兴趣的解决方案的域。
  • 如果给定函数是一个关系式 (>=, <=, >, <),且域为实数,则使用 solve_univariate_inequality 并返回解。目前尚不支持解决复数不等式的复数解,例如 (x² < 0)。
  • 基于 domain,方程被分派到 solveset_realsolveset_complex 中的一个函数来解决,分别在复数域或实数域中解决给定的方程。
  • 如果给定表达式是两个或更多函数的乘积,比如说 (gh = 0),那么给定方程的解是方程 (g = 0) 和 (h = 0) 的并集,当且仅当 (g) 和 (h) 都对有限输入有限时。因此,解是递归构建的。
  • 如果函数是三角函数或双曲函数,则调用函数 _solve_real_trig,通过将其转换为复数指数形式来解决它。
  • 现在检查函数是否存在 Piecewise 表达式的任何实例,如果有,则将其转换为显式表达式和设置对,并进行递归求解。
  • 相应的求解器现在尝试使用例程 invert_realinvert_complex 反转方程。这些例程基于数学反函数的概念(虽然不完全相同)。它将实数/复数值方程 (f(x) = y) 简化为一组方程:({g(x) = h_1(y), g(x) = h_2(y), ..., g(x) = h_n(y) }),其中 (g(x)) 比 (f(x)) 更简单。这需要一些工作来查找更复杂表达式的反函数。
  • 在反转之后,方程检查是否有根式或绝对值(模)部分,然后方法 _solve_radical 尝试通过诸如平方、立方等技术简化根式,并且 _solve_abs 迭代地解决嵌套的绝对值。
  • 如果上述方法都不成功,则使用多项式的方法如下:
    • 解决有理函数的方法 _solve_as_rational 被调用。根据定义域,分别调用相应的多项式求解器 _solve_as_poly_real_solve_as_poly_complex 来将 f 作为多项式求解。
    • 底层方法 _solve_as_poly 使用多项式技术解方程,如果它已经是多项式方程或者可以通过变量变换变成多项式方程。
  • solveset 返回的最终解集是上述解集和输入定义域的交集。

如何操作并返回无限解?

  • 在实数域中,我们使用集合模块中的 ImageSet 类返回无限解。ImageSet 是数学函数下的一个集合的图像。例如,要表示方程 (\sin{(x)} = 0) 的解,我们可以使用 ImageSet 如下:

  • >>> from sympy import ImageSet, Lambda, pi, S, Dummy, pprint
    
    >>> n = Dummy('n')
    
    >>> pprint(ImageSet(Lambda(n, 2*pi*n), S.Integers), use_unicode=True)
    
    {2⋅n⋅π │ n ∊ ℤ} 
    

    这里 n 是一个虚拟变量。基本上是整数集合在函数 (2\pi n) 下的图像。

  • 在复数域中,我们使用复数集合,它们在集合模块中实现为 ComplexRegion 类,用来表示阿格朗平面中的无限解。例如,要表示方程 (|z| = 1) 的解,即单位圆,我们可以使用 ComplexRegion 如下:

  • >>> from sympy import ComplexRegion, FiniteSet, Interval, pi, pprint
    
    >>> pprint(ComplexRegion(FiniteSet(1)*Interval(0, 2*pi), polar=True), use_unicode=True)
    
    {r⋅(ⅈ⋅sin(θ) + cos(θ)) │ r, θ ∊ {1} × 0, 2⋅π)} 
    

    FiniteSetProductSet 中是 (r) 的值域,即圆的半径,而 Interval 是 (\theta) 的范围,表示阿格朗平面中单位圆上的角度。

    注意:我们还有用于以直角坐标形式表示解的非极坐标形式符号。例如,在阿格朗平面的前两象限中表示 ComplexRegion,我们可以写成:

    >>> from sympy import ComplexRegion, Interval, pi, oo, pprint
    
    >>> pprint(ComplexRegion(Interval(-oo, oo)*Interval(0, oo)), use_unicode=True)
    
    {x + y⋅ⅈ │ x, y ∊ (-∞, ∞) × [0, ∞)} 
    

    其中区间是复数集合 (x + iy) 的 (x) 和 (y) 的范围。

solveset 如何确保不返回任何错误的解?

计算代数系统中的求解器基于启发式算法,因此通常很难确保在每种可能情况下都能达到 100%的正确性。然而,在许多情况下,我们仍然可以确保正确性。Solveset 尝试在可能的情况下验证正确性。例如:

考虑方程 (|x| = n)。解这个方程的一个天真的方法会返回 {-n, n} 作为其解,这是不正确的,因为只有当 n 是正数时,{-n, n} 才可能是其解。Solveset 也会返回这些信息以确保正确性。

>>> from sympy import symbols, S, pprint, solveset

>>> x, n = symbols('x, n')

>>> pprint(solveset(abs(x) - n, x, domain=S.Reals), use_unicode=True)

{x │ x ∊ {-n, n} ∧ (n ∈ [0, ∞))} 

尽管在这方面仍需大量工作。

基于搜索的求解器和逐步解决方案

注意:这还在开发中。

引入 [ConditionSet 后,解方程可以看作是集合转换。以下是我们可以用来解方程的抽象视图。

  • 在给定集合上应用各种集合转换。
  • 定义解的可用性度量,或者某些解比其他解更好的概念。
  • 不同的转换可以是树的节点。
  • 可以应用适当的搜索技术来获得最佳解决方案。

ConditionSet 给我们提供了以形如 ({x|f(x)=0; x \in S}) 和 ({x|f(x)>0; x \in S}) 的形式表示未评估的方程和不等式的能力,但 ConditionSet 更强大的地方在于它允许我们将中间步骤写为集合到集合的转换。一些转换包括:

  • 组合:({x|f(g(x))=0;x \in S} \Rightarrow {x|g(x)=y; x \in S, y \in {z|f(z)=0; z \in S}})

  • 多项式求解器:({x | P(x) = 0;x \in S} \Rightarrow {x_1,x_2, ... ,x_n} \cap S),

  • 其中 (x_i) 是 (P(x)) 的根。

  • 反转求解器:({x|f(x)=0;x \in S} \Rightarrow {g(0)| \text{ 所有的 } g \text{ 使得 } f(g(x)) = x})

  • logcombine:({x| \log(f(x)) + \log(g(x));x \in S})

  • (\Rightarrow {x| \log(f(x).g(x)); x \in S} \text{ 如果 } f(x) > 0 \text{ 和 } g(x) > 0) (\Rightarrow {x| \log(f(x)) + \log(g(x));x \in S} \text{ 否则})

  • 产品求解:({x|f(x)g(x)=0; x \in S})

  • (\Rightarrow {x|f(x)=0; x \in S} \cup {x|g(x)=0; x \in S}) (\text{ 给定 } f(x) \text{ 和 } g(x) \text{ 是有界的。}) (\Rightarrow {x|f(x)g(x)=0; x \in S}, \text{ 否则})

由于输出类型与输入类型相同,这些变换的任何组合也是有效的变换。我们的目标是找到变换序列(给定原子),将给定的条件集转换为非条件集,如 FiniteSet、Interval、整数集及其并、交、补或图像集。我们可以为每个集合分配一个成本函数,使得我们对这种形式的集合越喜欢,成本函数的值就越小。这样,我们的问题现在被简化为在图中找到从初始 ConditionSet 到值最低的集合的路径,其中原子变换形成边。

如何处理仅部分解已知的情况?

创建一个通用方程求解器,它可以解决我们在数学中遇到的每一个方程,是计算代数系统中的理想情况。对于无法解决或只能部分解决的情况,使用 ConditionSet 并充当未评估的 solveset 对象。

请注意,在数学上,找到方程的完全解集是不可判定的。参见 Richardson's theorem

ConditionSet 基本上是满足给定条件的元素集合。例如,要表示实域中方程的解:

[(x² - 4)(\sin(x) + x)]

我们可以表示为:

({-2, 2} ∪ {x | x \in \mathbb{R} ∧ x + \sin(x) = 0})

对于 solve 和 solveset 的计划是什么?

solveset 仍然不能做到的事情有些,而 solve 可以,比如解非线性多元和 LambertW 类型的方程。因此,它还不能完全替代 solve。随着 solveset 中的算法成熟,solveset 可能能够在 solve 中替换一些算法。

如何处理解集中的符号参数?

Solveset 处于初步开发阶段,因此并非所有情况下符号参数处理得当,但在这方面已经做了一些工作来表达我们对符号参数的思想。例如,考虑对于实数 (x) 解 (|x| = n),其中 (n) 是符号参数。Solveset 根据符号参数 (n) 的定义域返回 (x) 的值:

([0, \infty) \cap {n}) \cup ((-\infty, 0] \cap {-n}).]

这简单地意味着当 (n) 属于 Interval ([0, \infty)) 时,(n) 是解,当 (-n) 属于 Interval ((- \infty, 0]) 时,(-n) 是解。

还有其他情况需要处理,例如解 (2^x + (a - 2)) 对 (x) 的方程,其中 (a) 是符号参数。目前,它返回解作为与 (\mathbb{R}) 的交集,这是微不足道的,因为它不反映 (a) 在解中的定义域。

最近,我们还实现了一个在表达式的有限集(与区间的交集)中找到其非空域的函数。这对处理符号参数很有用。例如:

>>> from sympy import Symbol, FiniteSet, Interval, not_empty_in, sqrt, oo

>>> from sympy.abc import x

>>> not_empty_in(FiniteSet(x/2).intersect(Interval(0, 1)), x)

Interval(0, 2)

>>> not_empty_in(FiniteSet(x, x**2).intersect(Interval(1, 2)), x)

Union(Interval(1, 2), Interval(-sqrt(2), -1)) 

参考

Solveset 模块参考

使用 solveset() 解方程或表达式(假定等于 0)的单变量。可以如下解方程 (x² == 1):

>>> from sympy import solveset
>>> from sympy import Symbol, Eq
>>> x = Symbol('x')
>>> solveset(Eq(x**2, 1), x)
{-1, 1} 

或者可以手动将方程重写为等于 0 的表达式:

>>> solveset(x**2 - 1, x)
{-1, 1} 

solveset() 的第一个参数是一个表达式(等于零)或一个方程,第二个参数是要解方程的符号。

sympy.solvers.solveset.solveset(f, symbol=None, domain=Complexes)

解给定的不等式或方程,输出为集合

参数:

f:表达式或关系。

目标方程或不等式

symbol:符号

要解方程的变量

domain:集合

方程的解决域

返回:

集合

一个值的集合 (symbol),其中 (f) 为 True 或等于零。如果 (f) 为 False 或非零,则返回 EmptySet。如果尚未实现评估完整解决方案的算法,则返回 ConditionSet 作为未解决的对象。

solveset 声称其返回的解集是完整的。

引发:

NotImplementedError

尚未实现在复杂域中解决不等式的算法。

ValueError

输入无效。

RuntimeError

这是一个错误,请报告给 GitHub 问题跟踪器。

注释

Python 将 0 和 1 解释为 False 和 True,但在此函数中,它们分别指的是表达式的解。因此,0 和 1 分别返回域和 EmptySet,而 True 和 False 返回相反的值(因为它们被假定为关系表达式的解)。

示例

>>> from sympy import exp, sin, Symbol, pprint, S, Eq
>>> from sympy.solvers.solveset import solveset, solveset_real 
  • 默认域为复数域。不指定域将导致在复数域中解方程(这与符号上的假设无关):
>>> x = Symbol('x')
>>> pprint(solveset(exp(x) - 1, x), use_unicode=False)
{2*n*I*pi | n in Integers} 
>>> x = Symbol('x', real=True)
>>> pprint(solveset(exp(x) - 1, x), use_unicode=False)
{2*n*I*pi | n in Integers} 
  • 如果要使用 solveset 在实数域中解决方程,请提供一个实数域。(使用 solveset_real 可自动完成此操作。)
>>> R = S.Reals
>>> x = Symbol('x')
>>> solveset(exp(x) - 1, x, R)
{0}
>>> solveset_real(exp(x) - 1, x)
{0} 

解决方案不受符号假设的影响:

>>> p = Symbol('p', positive=True)
>>> pprint(solveset(p**2 - 4))
{-2, 2} 

当返回一个 ConditionSet 时,将具有可能改变集合的假设的符号替换为更通用的符号:

>>> i = Symbol('i', imaginary=True)
>>> solveset(Eq(i**2 + i*sin(i), 1), i, domain=S.Reals)
ConditionSet(_R, Eq(_R**2 + _R*sin(_R) - 1, 0), Reals) 
  • 只能在实数域上解决不等式。使用复数域会导致 NotImplementedError。
>>> solveset(exp(x) > 1, x, R)
Interval.open(0, oo) 

另请参阅

solveset_real

实数域求解器

solveset_complex

用于复数域的求解器

sympy.solvers.solveset.solveset_real(f, symbol)
sympy.solvers.solveset.solveset_complex(f, symbol)
sympy.solvers.solveset.invert_real(f_x, y, x)

反转一个实值函数。与 invert_complex() 相同,但在反转之前将域设置为 S.Reals

sympy.solvers.solveset.invert_complex(f_x, y, x, domain=Complexes)

将复值方程 (f(x) = y) 简化为一组方程

[\left{g(x) = h_1(y),\ g(x) = h_2(y),\ \dots,\ g(x) = h_n(y) \right}]

其中 (g(x)) 是比 (f(x)) 更简单的函数。返回值是一个元组 ((g(x), \mathrm{set}_h)),其中 (g(x)) 是 (x) 的函数,(\mathrm{set}_h) 是函数集合 (\left{h_1(y), h_2(y), \dots, h_n(y)\right})。这里,(y) 不一定是一个符号。

(\mathrm{set}_h) 包含函数及其在有效域内的信息,通过集合运算获得。例如,如果在实数域中反转 (y = |x| - n),那么 (\mathrm{set}_h) 不仅仅是 ({-n, n}),因为 (n) 的性质未知;而是:

left(left[0, \infty\right) \cap \left\{n\right\}\right) \cup

left(-\infty, 0\right] \cap \left\{- n\right\}

默认情况下,使用复数域,这意味着即使是看似简单的函数如 (\exp(x)),其在复数域中通过 (\log) 的反演结果与在实数域中获得的结果也大不相同。(对于 (\exp(x)),在复数域中通过 (\log) 的反演是多值的,具有无限多个分支。)

如果只处理实数值(或者不确定使用哪个函数),你可能应该将域设置为 S.Reals(或使用自动执行此操作的 invert_real)。

示例

>>> from sympy.solvers.solveset import invert_complex, invert_real
>>> from sympy.abc import x, y
>>> from sympy import exp 

何时 exp(x) == y?

>>> invert_complex(exp(x), y, x)
(x, ImageSet(Lambda(_n, I*(2*_n*pi + arg(y)) + log(Abs(y))), Integers))
>>> invert_real(exp(x), y, x)
(x, Intersection({log(y)}, Reals)) 

何时 exp(x) == 1?

>>> invert_complex(exp(x), 1, x)
(x, ImageSet(Lambda(_n, 2*_n*I*pi), Integers))
>>> invert_real(exp(x), 1, x)
(x, {0}) 

参见

invert_real, invert_complex

sympy.solvers.solveset.domain_check(f, symbol, p)

如果点 p 是无限的,或者 f 的任何子表达式是无限的,或者在用 p 替换符号后变为无限,则返回 False。如果没有满足这些条件的情况,则返回 True。

示例

>>> from sympy import Mul, oo
>>> from sympy.abc import x
>>> from sympy.solvers.solveset import domain_check
>>> g = 1/(1 + (1/(x + 1))**2)
>>> domain_check(g, x, -1)
False
>>> domain_check(x**2, x, 0)
True
>>> domain_check(1/x, x, oo)
False 
  • 该函数依赖于假设,即方程的原始形式未被自动简化改变。
>>> domain_check(x/x, x, 0) # x/x is automatically simplified to 1
True 
  • 要处理自动评估,请使用 evaluate=False:
>>> domain_check(Mul(x, 1/x, evaluate=False), x, 0)
False 
sympy.solvers.solveset.solvify(f, symbol, domain)

使用 solveset 解方程并根据 (solve) 输出 API 返回解。

返回:

我们根据 (solveset) 返回的解类型对输出进行分类。

抛出:

未实现错误

ConditionSet 是输入。

解决方案 | 输出

FiniteSet | 列表

ImageSet,| 列表(如果 (f) 是周期的)并集 |

并集 | 列表(包含 FiniteSet)

EmptySet | 空列表

其他 | 无

示例

>>> from sympy.solvers.solveset import solvify
>>> from sympy.abc import x
>>> from sympy import S, tan, sin, exp
>>> solvify(x**2 - 9, x, S.Reals)
[-3, 3]
>>> solvify(sin(x) - 1, x, S.Reals)
[pi/2]
>>> solvify(tan(x), x, S.Reals)
[0]
>>> solvify(exp(x) - 1, x, S.Complexes) 
>>> solvify(exp(x) - 1, x, S.Reals)
[0] 
sympy.solvers.solveset.linear_eq_to_matrix(equations, *symbols)

将给定的方程组转换为矩阵形式。这里的 (equations) 必须是符号 (symbols) 的线性方程组。元素 M[i, j] 对应于第 (i) 个方程中第 (j) 个符号的系数。

矩阵形式对应于增广矩阵形式。例如:

[4x + 2y + 3z = 1][3x + y + z = -6][2x + 4y + 9z = 2]

此系统将返回 A 和 b 为:

4 & 2 & 3 \ 3 & 1 & 1 \ 2 & 4 & 9 end{array}right] b = left[begin{array}{c} 1 \ -6 \ 2 end{array}right] $$ 执行的唯一简化是将`Eq(a, b)` \(\Rightarrow a - b\)转换。 Raises: **非线性错误** > 方程包含一个非线性项。 **数值错误** > 符号未给出或不唯一。 示例 ```py >>> from sympy import linear_eq_to_matrix, symbols >>> c, x, y, z = symbols('c, x, y, z') ``` 符号的系数(数字或符号)将作为矩阵返回: ```py >>> eqns = [c*x + z - 1 - c, y + z, x - y] >>> A, b = linear_eq_to_matrix(eqns, [x, y, z]) >>> A Matrix([ [c, 0, 1], [0, 1, 1], [1, -1, 0]]) >>> b Matrix([ [c + 1], [ 0], [ 0]]) ``` 此例程不会简化表达式,并且如果遇到非线性,则会引发错误: > ```py > >>> eqns = [ > > ... (x**2 - 3*x)/(x - 3) - 3, > > ... y**2 - 3*y - y*(y - 4) + x - 4] > > >>> linear_eq_to_matrix(eqns, [x, y]) > > Traceback (most recent call last): > > ... > > NonlinearError: > > symbol-dependent term can be ignored using `strict=False` > ``` > > 简化这些方程将丢弃第一个可去的奇异性,并显示第二个的线性结构: > > ```py > >>> [e.simplify() for e in eqns] > > [x - 3, x + y - 4] > ``` > > 必须在调用此例程之前执行任何这样的简化,以消除非线性项。 ```py sympy.solvers.solveset.linsolve(system, *symbols) ``` 解 N 个线性方程组,M 个变量;支持欠定和超定系统。可能的解数为零、一或无限。零解引发 ValueError,而无限解以给定符号的参数形式表示。对于唯一解,返回有序元组的`FiniteSet`。 支持所有标准输入格式:对于给定的方程组,给出了相应的输入类型如下: \[3x + 2y - z = 1\]\[2x - 2y + 4z = -2\]\[2x - y + 2z = 0\] + 增广矩阵形式,`system`如下所示: $$ text{system} = left[{array}{cccc} 3 & 2 & -1 & 1\ 2 & -2 & 4 & -2\ 2 & -1 & 2 & 0 end{array}right] $$ ```py system = Matrix([[3, 2, -1, 1], [2, -2, 4, -2], [2, -1, 2, 0]]) ``` + 方程列表形式 ```py system = [3x + 2y - z - 1, 2x - 2y + 4z + 2, 2x - y + 2z] ``` + 输入的 A 和 b 以矩阵形式(从 Ax = b)给出: $$ A = left[begin{array}{ccc} 3 & 2 & -1 \ 2 & -2 & 4 \ 2 & -1 & 2 end{array}right] b = left[begin{array}{c} 1 \ -2 \ 0 end{array}right] $$ ```py A = Matrix([[3, 2, -1], [2, -2, 4], [2, -1, 2]]) b = Matrix([[1], [-2], [0]]) system = (A, b) ``` 符号始终可以传递,但实际上只有在以下两种情况下才需要:1)传递一个方程组并且 2)将系统作为欠定矩阵传递,并且希望控制结果中自由变量的名称。如果对于情况 1 没有使用符号,则会引发错误,但如果对于情况 2 没有提供符号,则会提供内部生成的符号。在为情况 2 提供符号时,矩阵 A 中的列数至少应与符号相同。 这里使用的算法是高斯-约当消元法,消元后得到行梯形形式的矩阵。 返回: 包含有序元组值的有限集 FiniteSet 未知数为\(system\)具有解决方案。(包裹 FiniteSet 中的元组用于维护一致的 解决方案格式始终贯穿 solveset。 如果线性系统不一致,则返回 EmptySet。 Raises: **数值错误** > 输入无效。符号未给出。 示例 ```py >>> from sympy import Matrix, linsolve, symbols >>> x, y, z = symbols("x, y, z") >>> A = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 10]]) >>> b = Matrix([3, 6, 9]) >>> A Matrix([ [1, 2, 3], [4, 5, 6], [7, 8, 10]]) >>> b Matrix([ [3], [6], [9]]) >>> linsolve((A, b), [x, y, z]) {(-1, 2, 0)} ``` + 参数化解:如果系统欠定,函数将返回以给定符号为参数的参数解。自由的符号将保持不变。例如,在下面的系统中,\(z\)被返回为变量\(z\)的解;它可以取任何值。 ```py >>> A = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) >>> b = Matrix([3, 6, 9]) >>> linsolve((A, b), x, y, z) {(z - 1, 2 - 2*z, z)} ``` 如果没有给出符号,则将使用内部生成的符号。第三位置的`tau0`表示(与之前一样),第三个变量 - 无论其名称如何 - 可以取任何值: ```py >>> linsolve((A, b)) {(tau0 - 1, 2 - 2*tau0, tau0)} ``` + 作为输入的方程组列表 ```py >>> Eqns = [3*x + 2*y - z - 1, 2*x - 2*y + 4*z + 2, - x + y/2 - z] >>> linsolve(Eqns, x, y, z) {(1, -2, -2)} ``` + 作为输入的增广矩阵 ```py >>> aug = Matrix([[2, 1, 3, 1], [2, 6, 8, 3], [6, 8, 18, 5]]) >>> aug Matrix([ [2, 1, 3, 1], [2, 6, 8, 3], [6, 8, 18, 5]]) >>> linsolve(aug, x, y, z) {(3/10, 2/5, 0)} ``` + 为符号系数求解 ```py >>> a, b, c, d, e, f = symbols('a, b, c, d, e, f') >>> eqns = [a*x + b*y - c, d*x + e*y - f] >>> linsolve(eqns, x, y) {((-b*f + c*e)/(a*e - b*d), (a*f - c*d)/(a*e - b*d))} ``` + 退化系统返回给定符号的解集。 ```py >>> system = Matrix(([0, 0, 0], [0, 0, 0], [0, 0, 0])) >>> linsolve(system, x, y) {(x, y)} ``` + 对于空系统,linsolve 返回空集 ```py >>> linsolve([], x) EmptySet ``` + 如果检测到任何非线性,则会引发错误,即使可以通过扩展删除 ```py >>> linsolve([x*(1/x - 1)], x) Traceback (most recent call last): ... NonlinearError: nonlinear term: 1/x ``` ```py >>> linsolve([x*(y + 1)], x, y) Traceback (most recent call last): ... NonlinearError: nonlinear cross-term: x*(y + 1) ``` ```py >>> linsolve([x**2 - 1], x) Traceback (most recent call last): ... NonlinearError: nonlinear term: x**2 ``` ```py sympy.solvers.solveset.nonlinsolve(system, *symbols) ``` 解\(N\)个非线性方程与\(M\)个变量的系统,这意味着支持欠定和超定系统。也支持正维系统(系统具有无限多个解被称为正维系统)。在正维系统中,解将依赖于至少一个符号。返回实数解和复数解(如果存在)。 参数: **系统**:方程组列表 > 方程组的目标系统 **symbols**:符号列表 > 符号应作为序列给出,例如列表 返回: 一个`FiniteSet`的有序元组,其值为\(symbols\),以便\(system\) 有解。元组中的值顺序与符号出现的顺序相同 参数\(symbols\)。 请注意,一般的`FiniteSet`是无序的,解决方案 这里返回的解不仅仅是一个`FiniteSet`的解集,而是 是一个`FiniteSet`的有序元组,即第一个也是唯一的 `FiniteSet`的参数是一个解的元组,即 有序,因此,返回的解决方案是有序的。 还请注意,解也可以作为有序元组返回, FiniteSet 只是元组周围的包装 `{}`。没有其他 除了维持一致性外,没有其他重要性 解决方案的输出格式始终保持在解集中。 对于给定的方程组,各自的输入类型 给定如下: \[xy - 1 = 0\]\[4x² + y² - 5 = 0\] ```py system = [x*y - 1, 4*x**2 + y**2 - 5] symbols = [x, y] ``` 抛出: **ValueError** > 输入无效。未给出符号。 **AttributeError** > 输入的符号不是\(Symbol\)类型。 例子 ```py >>> from sympy import symbols, nonlinsolve >>> x, y, z = symbols('x, y, z', real=True) >>> nonlinsolve([x*y - 1, 4*x**2 + y**2 - 5], [x, y]) {(-1, -1), (-1/2, -2), (1/2, 2), (1, 1)} ``` 1. 正维系统和补集: ```py >>> from sympy import pprint >>> from sympy.polys.polytools import is_zero_dimensional >>> a, b, c, d = symbols('a, b, c, d', extended_real=True) >>> eq1 = a + b + c + d >>> eq2 = a*b + b*c + c*d + d*a >>> eq3 = a*b*c + b*c*d + c*d*a + d*a*b >>> eq4 = a*b*c*d - 1 >>> system = [eq1, eq2, eq3, eq4] >>> is_zero_dimensional(system) False >>> pprint(nonlinsolve(system, [a, b, c, d]), use_unicode=False) -1 1 1 -1 {(---, -d, -, {d} \ {0}), (-, -d, ---, {d} \ {0})} d d d d >>> nonlinsolve([(x+y)**2 - 4, x + y - 2], [x, y]) {(2 - y, y)} ``` 2\. 如果一些方程不是多项式,则\(nonlinsolve\)将调用`substitution`函数并返回实数和复数解(如果存在)。 ```py >>> from sympy import exp, sin >>> nonlinsolve([exp(x) - sin(y), y**2 - 4], [x, y]) {(ImageSet(Lambda(_n, I*(2*_n*pi + pi) + log(sin(2))), Integers), -2), (ImageSet(Lambda(_n, 2*_n*I*pi + log(sin(2))), Integers), 2)} ``` 3\. 如果系统是非线性多项式且零维的,则使用`solve_poly_system()`返回解(如果有实数和复数解): ```py >>> from sympy import sqrt >>> nonlinsolve([x**2 - 2*y**2 -2, x*y - 2], [x, y]) {(-2, -1), (2, 1), (-sqrt(2)*I, sqrt(2)*I), (sqrt(2)*I, -sqrt(2)*I)} ``` 4\. `nonlinsolve`可以解决一些线性系统(零维或正维),因为它使用`sympy.polys.polytools.groebner()`函数获取 Groebner 基础,然后使用`substitution`函数将基础作为新系统。但不推荐使用`nonlinsolve`解决线性系统,因为一般的线性系统最好使用`linsolve()`。 ```py >>> nonlinsolve([x + 2*y -z - 3, x - y - 4*z + 9, y + z - 4], [x, y, z]) {(3*z - 5, 4 - z, z)} ``` 5\. 只有多项式方程且仅有实数解的系统使用`solve_poly_system()`求解: ```py >>> e1 = sqrt(x**2 + y**2) - 10 >>> e2 = sqrt(y**2 + (-x + 10)**2) - 3 >>> nonlinsolve((e1, e2), (x, y)) {(191/20, -3*sqrt(391)/20), (191/20, 3*sqrt(391)/20)} >>> nonlinsolve([x**2 + 2/y - 2, x + y - 3], [x, y]) {(1, 2), (1 - sqrt(5), 2 + sqrt(5)), (1 + sqrt(5), 2 - sqrt(5))} >>> nonlinsolve([x**2 + 2/y - 2, x + y - 3], [y, x]) {(2, 1), (2 - sqrt(5), 1 + sqrt(5)), (2 + sqrt(5), 1 - sqrt(5))} ``` 6\. 最好使用符号而不是三角函数或`Function`。例如,用符号替换\(\sin(x)\),用符号替换\(f(x)\)等。从`nonlinsolve`获取解决方案,然后使用`solveset()`得到\(x\)的值。 比老解算器 `_solve_system` 更好的`nonlinsolve`: 1\. 正维系统求解器:`nonlinsolve`可以返回正维系统的解。它找到正维系统的 Groebner 基础(称为基础),然后我们可以开始解方程(首先解最少变量的方程),使用 solveset 并将已解决的解值代入其他方程(基础方程)中,以获得最小变量的解。重要的是我们如何替换已知值以及替换到哪些方程中。 2\. 实数和复数解:`nonlinsolve`返回实数和复数解。如果系统中的所有方程都是多项式,则使用`solve_poly_system()`返回实数和复数解。如果系统中的所有方程不是多项式方程,则使用`substitution`方法处理这些多项式和非多项式方程,以解决未解决的变量。在解决实数和复数解时,使用`solveset_real`和`solveset_complex`。对于实数和复数解,`_solve_using_known_values`在`substitution`内部使用(当存在非多项式方程时将调用`substitution`)。如果解是有效的,则将其一般解添加到最终结果中。 3\. `补集`和`交集`将被添加:nonlinsolve 在`substitution`函数执行期间保持补集和交集的字典。如果`solveset`在执行期间找到任何变量的补集或交集,那么在返回最终解之前将添加该变量的补集或交集。 ### transolve ```py sympy.solvers.solveset._transolve(f, symbol, domain) ``` 解超越方程的函数。这是`solveset`的一个辅助函数,应在内部使用。`_transolve`目前支持以下类型的方程: > + 指数方程 > + > + 对数方程 参数: **f**:需要解决的任何超越方程。 > 这应该是一个表达式,假设等于`0`。 **symbol**:要解方程的变量。 > 这需要是`Symbol`类的。 **domain**:方程解的集合。 > 这应该是`Set`类的。 返回: 集合 > `symbol`的一组值,使得`f`等于零。如果`f`在相应域中没有解,则返回`EmptySet`。如果尚未实现评估完整解的算法,则返回`ConditionSet`作为未解决的对象。 如何使用`_transolve` 不应独立使用`_transolve`作为一个独立函数,因为它假设方程(`f`)和`symbol`来自`solveset`并可能经历了一些修改。要将`_transolve`作为一个独立函数使用,方程(`f`)和`symbol`应作为它们由`solveset`传递的样子传递。 示例 ```py >>> from sympy.solvers.solveset import _transolve as transolve >>> from sympy.solvers.solvers import _tsolve as tsolve >>> from sympy import symbols, S, pprint >>> x = symbols('x', real=True) # assumption added >>> transolve(5**(x - 3) - 3**(2*x + 1), x, S.Reals) {-(log(3) + 3*log(5))/(-log(5) + 2*log(3))} ``` `_transolve`的工作原理 `_transolve`使用两种类型的辅助函数来解决特定类别的方程: 识别辅助函数:确定给定方程是否属于某一类方程。返回`True`或`False`。 解决辅助函数:一旦确定了方程,相应的辅助函数将解决方程或返回`solveset`更好处理的方程形式。 + 模块背后的哲学 `_transolve`的目的是接受生成器中不是多项式的方程,并通过有效的转换将它们转换为这样的形式,或者直接解决它们。每类支持的超越函数都使用一对辅助函数来实现这一目的。其中一个识别方程的超越形式,另一个要么解决它,要么重新将其重塑为`solveset`可以处理的可处理形式。例如,形如 \(ab^{f(x)} - cd^{g(x)} = 0\) 的方程在某些假设下可以转换为 \(\log(a) + f(x)\log(b) - \log(c) - g(x)\log(d) = 0\),并且如果 \(f(x)\) 和 \(g(x)\) 是多项式形式,则可以使用`solveset`解决。 `_transolve`为何优于`_tsolve` 1. 更好的输出 `_transolve`以更简化的形式提供表达式。 考虑一个简单的指数方程 ```py >>> f = 3**(2*x) - 2**(x + 3) >>> pprint(transolve(f, x, S.Reals), use_unicode=False) -3*log(2) {------------------} -2*log(3) + log(2) >>> pprint(tsolve(f, x), use_unicode=False) / 3 \ | --------| | log(2/9)| [-log\2 /] ``` 1. 可扩展的 `_transolve` 的 API 被设计成易于扩展,即解决给定类别方程的代码被封装在辅助函数中,而不是与 `_transolve` 代码混合在一起。 1. 模块化 `_transolve` 被设计为模块化,即为每一类方程实现一个单独的识别和解决助手。这样可以轻松修改或更改任何直接实现在辅助函数中的方法,而不会干扰 API 的实际结构。 1. 更快的计算 通过 `_transolve` 解方程要比通过 `_tsolve` 快得多。在 `solve` 中,尝试计算每一种可能性以获取解决方案。这一系列尝试使得解决过程有些慢。在 `_transolve` 中,只有在识别特定类型的方程后才开始计算。 如何添加新的方程类别 添加新的方程类别求解器是一个三步骤过程: + 确定方程的类型 确定它们所属的方程类别的类型:可以是 `Add`、`Pow` 等类型。使用不同的内部函数处理每种类型。编写识别和解决助手,并从例程中使用它们来处理给定类型的方程(如果需要添加)。 ```py def add_type(lhs, rhs, x): .... if _is_exponential(lhs, x): new_eq = _solve_exponential(lhs, rhs, x) .... rhs, lhs = eq.as_independent(x) if lhs.is_Add: result = add_type(lhs, rhs, x) ``` + 定义识别助手。 + 定义解决助手。 除此之外,添加方程求解器时需要注意的一些其他事项: + 命名约定:识别助手的名称应为 `_is_class`,其中类将是方程类别的名称或缩写。求解助手将命名为 `_solve_class`。例如,对于指数方程,它变为 `_is_exponential` 和 `_solve_expo`。 + 识别助手应该接受两个输入参数,待检查的方程和正在寻找解的变量,而解决助手则需要一个额外的域参数。 + 请务必考虑边界情况。 + 为每个辅助函数添加测试。 + 为您的辅助函数添加文档字符串,描述已实现的方法。辅助函数的文档应标识: + 辅助函数的目的, + 用于识别和解决方程的方法, + 一个正确性的证明 + 辅助函数的返回值 ```py sympy.solvers.solveset._is_exponential(f, symbol) ``` 如果一个或多个项仅包含 `symbol` 的指数,则返回 `True`,否则返回 `False`。 参数: **f** : 表达式 > 要检查的方程 **symbol** : 符号 > 检查方程的变量 示例 ```py >>> from sympy import symbols, cos, exp >>> from sympy.solvers.solveset import _is_exponential as check >>> x, y = symbols('x y') >>> check(y, y) False >>> check(x**y - 1, y) True >>> check(x**y*2**y - 1, y) True >>> check(exp(x + 3) + 3**x, x) True >>> check(cos(2**x), x) False ``` + 辅助函数背后的哲学 该函数提取方程的每一项,并检查它是否符合相对于 `symbol` 的指数形式。 ```py sympy.solvers.solveset._solve_exponential(lhs, rhs, symbol, domain) ``` 用于解决(支持的)指数方程的辅助函数。 指数方程目前最多由两项组成,其中一项或两项具有依赖于符号的指数。 例如 \[5^{2x + 3} - 5^{3x - 1}\]\[4^{5 - 9x} - e^{2 - x}\] 参数: **lhs, rhs** : 表达式 > 要解决的指数方程,\(lhs = rhs\) **symbol** : 符号 > 在其中解方程的变量 **domain** : 集合 > 一个解方程式的集合。 返回: 满足给定方程的解集。 如果方程无法解决,就会出现 `ConditionSet` 或 如果假设没有适当定义,在这种情况下 返回具有不同风格的 `ConditionSet`,其中 方程式的解(解)与所需的假设。 例子 ```py >>> from sympy.solvers.solveset import _solve_exponential as solve_expo >>> from sympy import symbols, S >>> x = symbols('x', real=True) >>> a, b = symbols('a b') >>> solve_expo(2**x + 3**x - 5**x, 0, x, S.Reals) # not solvable ConditionSet(x, Eq(2**x + 3**x - 5**x, 0), Reals) >>> solve_expo(a**x - b**x, 0, x, S.Reals) # solvable but incorrect assumptions ConditionSet(x, (a > 0) & (b > 0), {0}) >>> solve_expo(3**(2*x) - 2**(x + 3), 0, x, S.Reals) {-3*log(2)/(-2*log(3) + log(2))} >>> solve_expo(2**x - 4**x, 0, x, S.Reals) {0} ``` + 方法的正确性证明 对数函数是指数函数的反函数。指数和对数之间的定义关系是: \[{\log_b x} = y \enspace if \enspace b^y = x\] 因此,如果我们给出一个包含指数项的方程,我们可以将每个项转换为其相应的对数形式。这通过取对数和使用对数恒等式扩展方程来实现,以便 `solveset` 轻松处理。 例如: \[3^{2x} = 2^{x + 3}\] 对两边取对数将减少方程为 \[(2x)\log(3) = (x + 3)\log(2)\] 可以很容易地由 `solveset` 处理此形式。 ```py sympy.solvers.solveset._solve_logarithm(lhs, rhs, symbol, domain) ``` 辅助解决可以化简为单个 \(\log\) 实例的对数方程。 目前对数方程是包含可以使用各种对数恒等式将其化简为单个 \(\log\) 项或常数的方程。 例如: \[\log(x) + \log(x - 4)\] 可以简化为: \[\log(x(x - 4))\] 参数: **lhs, rhs** : 表达式 > 要解决的对数方程,\(lhs = rhs\) **symbol** : 符号 > 在其中解方程的变量 **domain** : 集合 > 一个解方程式的集合。 返回: 满足给定方程的解集。 如果方程无法解决,则返回 `ConditionSet`。 例子 ```py >>> from sympy import symbols, log, S >>> from sympy.solvers.solveset import _solve_logarithm as solve_log >>> x = symbols('x') >>> f = log(x - 3) + log(x + 3) >>> solve_log(f, 0, x, S.Reals) {-sqrt(10), sqrt(10)} ``` + 正确性的证明 对数是另一种写指数的方法,并且由以下定义 \[{\log_b x} = y \enspace if \enspace b^y = x\] 当方程的一边包含单个对数时,可以通过将方程重写为等效的指数方程来解决该方程,如上定义。但是,如果一边包含多个对数,则需要使用对数的性质将其压缩为单个对数。 举个例子 \[\log(2x) - 15 = 0\] 包含单个对数,因此我们可以直接将其重写为指数形式 \[x = \frac{e^{15}}{2}\] 但是如果方程具有多个对数,例如 \[\log(x - 3) + \log(x + 3) = 0\] 我们使用对数恒等式将其转换为简化形式 使用, \[\log(a) + \log(b) = \log(ab)\] 方程变为, \[\log((x - 3)(x + 3))\] 这个方程包含一个对数,可以通过重写为指数形式来解决。 ```py sympy.solvers.solveset._is_logarithmic(f, symbol) ``` 如果方程是 \(a\log(f(x)) + b\log(g(x)) + ... + c\) 形式,则返回 `True`,否则返回 `False`。 参数: **f** : 表达式 > 要检查的方程式 **symbol** : 符号 > 在其中检查方程的变量 返回: 如果方程是对数的,则返回 `True`。 例子 ```py >>> from sympy import symbols, tan, log >>> from sympy.solvers.solveset import _is_logarithmic as check >>> x, y = symbols('x y') >>> check(log(x + 2) - log(x + 3), x) True >>> check(tan(log(2*x)), x) False >>> check(x*log(x), x) False >>> check(x + log(x), x) False >>> check(y + log(x), x) True ``` + 辅助工具背后的哲学 该函数提取每个项并检查其是否对于`symbol`是对数的。 ## 丢番图方程(DEs) 查看丢番图方程 ## 不等式 查看不等式求解器 ## 普通微分方程(ODEs) 查看 ODE(常微分方程)。 ## 偏微分方程(PDEs) 查看 PDE(偏微分方程)。