Solveset

这是 solveset 模块在 solvers 中的官方文档。它包含了关于我们新模块解决方程的常见问题。

备注

对于一个专注于解决常见类型方程的初学者友好指南,请参阅 解方程

solve() 出了什么问题:

SymPy 已经有一个相当强大的 solve 函数。但它有一些不足之处。例如:

  1. 对于各种类型的解决方案,它没有一致的输出。它需要一致地返回多种类型的解决方案:

    • 单一解法 : \(x = 1\)

    • 多种解法:\(x^2 = 1\)

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

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

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

    • 具有点解的多变量函数:\(x^2 + y^2 = 0\)

    • 非点解的多变量函数:\(x^2 + y^2 = 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]\)

  • 在复数域和实数域的方程求解器之间,代码层面和接口层面有清晰的分离。例如,当 \(x\) 在复数域中求解 \(e^x = 1\) 时,返回所有解的集合,即 \(\{2 n i \pi | n \in \mathbb{Z}\}\),而如果 \(x\) 在实数域中求解,则只返回 \(\{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

    表示Argand平面中某个区域内的所有复数集合。

  • 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集合)能提供我们集合模块试图模拟的数学集合的灵活性。使用集合的第二个原因是它们接近数学家处理的对象,这使得更容易推理它们。集合对象尽可能符合Python的惯例,即,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 对象的集合来解决:

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

  • 使用字典可能会导致一些意想不到的结果,例如:

    • solve(Eq(x**2, 1), x) != solve(Eq(y**2, 1), y)

      从数学上讲,这是没有意义的。在这里使用 FiniteSet 解决了问题。

  • 它也不能表示像 \(|x| < 1\) 这样的方程的解,这在 Argand 平面上是一个半径为 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 用于解方程的一般方法有哪些?

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

  • domain 参数首先被考虑,以了解用户感兴趣获取解决方案的领域。

  • 如果给定的函数是关系式的(>=<=><),并且定义域是实数,那么 solve_univariate_inequality 会返回解。求解不等式的复数解,如 \(x^2 < 0\),目前尚不支持。

  • 基于 domain ,方程被分派到两个函数之一 solveset_realsolveset_complex ,它们分别在复数域或实数域中求解给定的方程。

  • 如果给定的表达式是两个或多个函数的乘积,例如 \(gh = 0\),那么给定方程的解是方程 \(g = 0\)\(h = 0\) 的解的并集,当且仅当 \(g\)\(h\) 对于有限输入都是有限的情况下。因此,解是通过递归构建的。

  • 如果函数是三角函数或双曲函数,则会调用函数 _solve_real_trig ,该函数通过将其转换为复指数形式来求解。

  • 现在检查函数中是否存在 Piecewise 表达式的实例,如果有,则将其转换为显式表达式和集合对,然后递归求解。

  • 各自的求解器现在尝试使用``invert_real``和``invert_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\) 下的像。

  • 在复数域中,我们使用复数集,这些复数集在sets模块中作为``ComplexRegion``类实现,以表示Argand平面中的无限解。例如,要表示方程`|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⋅π)}
    

    ProductSet 中,FiniteSet\(r\) 值的范围,\(r\) 是圆的半径,而 Interval 是 ` heta` 的范围,` heta` 是从 \(x\) 轴开始的角度,表示 Argand 平面中的单位圆。

    注意:我们也有非极坐标形式的表示法来表示矩形形式中的解。例如,要在Argand平面上表示前两个象限,我们可以将 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{ 所有满足 } f(g(x)) = x \text{ 的 } g\}\)

  • 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\} U \{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{ 否则}\)

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

我们如何处理只有部分解决方案已知的情况?

创建一个能够解决我们在数学中遇到的所有方程的通用方程求解器,是计算机代数系统中求解器的一个理想情况。当遇到无法解决或只能不完全解决的情况时,会使用 ConditionSet,并作为未求解的 solveset 对象。

需要注意的是,从数学上讲,找到一个方程的完整解集是不可判定的。参见 Richardson定理

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

\[(x^2 - 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中,符号参数是如何处理的?

Solveset 目前处于开发的初期阶段,因此对于所有情况下的符号参数处理得并不完善,但我们在这方面已经做了一些工作,以体现我们对符号参数的理念。例如,考虑对实数 \(x\) 求解 \(|x| = n\),其中 \(n\) 是一个符号参数。Solveset 在返回 \(x\) 的值时,也会考虑符号参数 \(n\) 的定义域:

\[ \begin{align}\begin{aligned}([0, \infty) \cap \{n\}) \cup ((-\infty, 0] \cap \{-n\}).\\([0, \infty) \cap \{n\}) \cup ((-\infty, 0] \cap \{-n\}).\end{aligned}\end{align} \]

这意味着 \(n\) 仅当它属于 区间 \([0, \infty)\) 时是解,而 \(-n\) 仅当 \(-n\) 属于 区间 \((-\infty, 0]\) 时是解。

还有其他情况需要处理,比如求解 \(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^2 == 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\) 的值,其中 \(f\) 为 True 或等于零。如果 \(f\) 为 False 或非零,则返回 EmptySet。如果尚未实现评估完整解的算法,则返回 ConditionSet 作为未解对象。

solveset 声称在其返回的解集中是完整的。
Raises:
NotImplementedError

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

ValueError

输入无效。

RuntimeError

这是一个错误,请报告到github问题追踪器。

参见

solveset_real

实数域求解器

solveset_complex

复杂域的求解器

注释

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)
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, inftyright) cap left{nright}right) cup

left(left(-infty, 0right] cap left{- nright}right)$$

默认情况下,使用的是复数域,这意味着即使是对看似简单的函数(如 \(\exp(x)\))进行求逆,也会得到与实数域中截然不同的结果。(以 \(\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})
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\) 返回的解的类型对输出进行分类。
Raises:
NotImplementedError

ConditionSet 是输入。

示例

>>> 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\) 为:

$$ A = left[begin{array}{ccc}

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:
非线性误差

这些方程包含一个非线性项。

ValueError

符号未给出或不唯一。

示例

>>> from sympy import linear_eq_to_matrix, symbols
>>> c, x, y, z = symbols('c, x, y, z')

符号的系数(数值或符号)将作为矩阵返回:

>>> 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]])

此例程不会简化表达式,并且在遇到非线性时会引发错误:

>>> 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`

简化这些方程将消除第一个中的可去奇点,并揭示第二个的线性结构:

>>> [e.simplify() for e in eqns]
[x - 3, x + y - 4]

任何需要消除非线性项的简化工作都必须在调用此例程 之前 完成。

sympy.solvers.solveset.linsolve(system, *symbols)[源代码][源代码]

求解具有 \(M\) 个变量的 \(N\) 个线性方程组;支持欠定系统和超定系统。可能的解的数量为零、一个或无穷。零解会抛出 ValueError,而无穷解则根据给定的符号参数化表示。对于唯一解,返回一个有序元组的 FiniteSet

所有标准输入格式都支持:对于给定的方程组,相应的输入类型如下:

\[3x + 2y - z = 1\]
\[2x - 2y + 4z = -2\]
\[2x - y + 2z = 0\]
  • 增广矩阵形式,如下所示的 system

$$ text{系统} = left[{array}{cccc}

3 & 2 & -1 & 1\ 2 & -2 & 4 & -2\ 2 & -1 & 2 & 0 end{array}right] $$

system = Matrix([[3, 2, -1, 1], [2, -2, 4, -2], [2, -1, 2, 0]])
  • 方程列表形式

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] $$

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中的列数相同。

这里使用的算法是高斯-约旦消元法,消元后得到的是行阶梯形矩阵。

返回:
包含一个有序元组值的有限集合
\(system\) 有解决方案的未知数。(包装
在 FiniteSet 中,元组用于保持一致性。
在整个 solveset 中输出格式。
如果线性系统不一致,则返回 EmptySet。
Raises:
ValueError

输入无效。未给出符号。

示例

>>> 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 的解返回;它可以取任何值。

>>> 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 表示(如前所述)第三个变量——无论其名称是什么——可以取任何值:

>>> linsolve((A, b))
{(tau0 - 1, 2 - 2*tau0, tau0)}
  • 输入的方程列表

>>> 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)}
  • 增广矩阵作为输入

>>> 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)}
  • 求解符号系数

>>> 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))}
  • 退化系统将解返回为给定符号的集合。

>>> system = Matrix(([0, 0, 0], [0, 0, 0], [0, 0, 0]))
>>> linsolve(system, x, y)
{(x, y)}
  • 对于一个空系统,linsolve 返回空集

>>> linsolve([], x)
EmptySet
  • 如果检测到任何非线性,即使可以通过扩展消除,也会引发错误

>>> linsolve([x*(1/x - 1)], x)
Traceback (most recent call last):
...
NonlinearError: nonlinear term: 1/x
>>> linsolve([x*(y + 1)], x, y)
Traceback (most recent call last):
...
NonlinearError: nonlinear cross-term: x*(y + 1)
>>> linsolve([x**2 - 1], x)
Traceback (most recent call last):
...
NonlinearError: nonlinear term: x**2
sympy.solvers.solveset.nonlinsolve(system, *symbols)[源代码][源代码]

求解具有 \(M\) 个变量的 \(N\) 个非线性方程组,这意味着支持欠定和超定系统。还支持正维系统(具有无限多解的系统称为正维系统)。在正维系统中,解将至少依赖于一个符号。返回实数解和复数解(如果存在)。

参数:
系统方程列表

方程组的目标系统

符号符号列表

符号应作为序列提供,例如列表

返回:
一个有序元组值的 FiniteSet,这些值对应于 \(symbols\),用于 \(system\)
有解决方案。元组中值的顺序与存在的符号相同。
参数 \(symbols\)
请注意,一般的 FiniteSet 是无序的,解决方案
返回的不仅仅是一个 FiniteSet 的解,而是
是一个有序元组的 FiniteSet ,即第一个且唯一的一个
传递给 FiniteSet 的参数是一个解的元组,该元组是
有序的,因此,返回的解决方案是有序的。
另请注意,解决方案也可以作为有序元组返回,
FiniteSet 只是围绕元组 {} 的一个包装。它没有其他功能。
除了用于保持一致性这一事实外,其重要性
在整个 solveset 中的输出格式。
对于给定的一组方程,相应的输入类型
如下所示:
\[xy - 1 = 0 ..\]
\[4x^2 + y^2 - 5 = 0 ..\]
::

system = [x*y - 1, 4*x**2 + y**2 - 5] symbols = [x, y]

Raises:
ValueError

输入无效。未给出符号。

AttributeError

输入的符号不是 \(Symbol\) 类型。

示例

>>> 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. 正维系统及其补集:

>>> 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. If some of the equations are non-polynomial then \(nonlinsolve\) will call the substitution function and return real and complex solutions, if present.

>>> 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. If system is non-linear polynomial and zero-dimensional then it returns both solution (real and complex solutions, if present) using solve_poly_system():

>>> 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 can solve some linear (zero or positive dimensional) system (because it uses the sympy.polys.polytools.groebner() function to get the groebner basis and then uses the substitution function basis as the new \(system\)). But it is not recommended to solve linear system using nonlinsolve, because linsolve() is better for general linear systems.

>>> nonlinsolve([x + 2*y -z - 3, x - y - 4*z + 9, y + z - 4], [x, y, z])
{(3*z - 5, 4 - z, z)}

5. System having polynomial equations and only real solution is solved using solve_poly_system():

>>> 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. It is better to use symbols instead of trigonometric functions or Function. For example, replace \(\sin(x)\) with a symbol, replace \(f(x)\) with a symbol and so on. Get a solution from nonlinsolve and then use solveset() to get the value of \(x\).

transolve

sympy.solvers.solveset._transolve(f, symbol, domain)[源代码][源代码]

解决超越方程的函数。它是 solveset 的辅助函数,应在内部使用。_transolve 目前支持以下类型的方程:

  • 指数方程

  • 对数方程

参数:
f任何需要求解的超越方程。

这需要是一个表达式,假设它等于 0

符号方程求解的变量。

这需要是 Symbol 类的。

领域方程求解的集合。

这需要是 Set 类。

返回:
设置

symbol 的一组值,使得 f 等于零。如果 f 在相应域中没有解,则返回 EmptySet。如果尚未实现评估完整解的算法,则返回 ConditionSet 作为未解决的对象。

示例

>>> 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))}
sympy.solvers.solveset._is_exponential(f, symbol)[源代码][源代码]

如果一个或多个项仅在指数中包含 symbol ,则返回 True ,否则返回 False

参数:
f表达式

待检查的方程

符号符号

检查方程的变量

示例

>>> 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 的指数形式。

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\)

符号符号

求解方程的变量

领域设置

方程求解的集合。

返回:
满足给定方程的一组解。
如果方程无解,则为 ConditionSet
如果假设没有被正确地定义,在这种情况下
返回的是一种不同风格的 ConditionSet
满足所需假设的方程的解。

示例

>>> 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 如果 \enspace b^y = x\]

因此,如果我们得到一个带有指数项的方程,我们可以将每一项转换为其对应的对数形式。这是通过取对数并使用对数恒等式展开方程来实现的,以便可以由 solveset 轻松处理。

例如:

\[3^{2x} = 2^{x + 3}\]

两边取对数将简化方程为

\[(2x)\log(3) = (x + 3)\log(2)\]

这个形式可以很容易地由 solveset 处理。

sympy.solvers.solveset._solve_logarithm(lhs, rhs, symbol, domain)[源代码][源代码]

帮助解决可简化为单个 \(\log\) 实例的对数方程的辅助工具。

对数方程是(目前)包含 \(\log\) 项的方程,这些项可以通过各种对数恒等式简化为一个 \(\log\) 项或一个常数。

例如:

\[\log(x) + \log(x - 4)\]

可以简化为:

\[\log(x(x - 4))\]
参数:
lhs, rhs表达式

要解决的对数方程,\(lhs = rhs\)

符号符号

求解方程的变量

领域设置

方程求解的集合。

返回:
满足给定方程的一组解。
如果方程无解,则返回 ConditionSet

示例

>>> 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 如果 \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))\]

这个方程包含一个对数,可以通过重写为指数来求解。

sympy.solvers.solveset._is_logarithmic(f, symbol)[源代码][源代码]

如果方程为 \(a\log(f(x)) + b\log(g(x)) + ... + c\) 的形式,则返回 True,否则返回 False

参数:
f表达式

待检查的方程

符号符号

检查方程的变量

返回:
True 如果方程是 logarithmic 否则 False

示例

>>> 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