SymPy 1.13 中文文档(十一)
原文:
docs.sympy.org/latest/index.html
离散
原文链接:
docs.sympy.org/latest/modules/discrete.html
在 SymPy 的discrete
模块中实现了计算有限序列的离散变换和卷积的方法。
该模块包含对离散序列操作的函数。
Transforms - fft
, ifft
, ntt
, intt
, fwht
, ifwht
,
mobius_transform
, inverse_mobius_transform
卷积 - convolution
, convolution_fft
, convolution_ntt
,
convolution_fwht
, convolution_subset
, covering_product
, intersecting_product
由于离散变换可用于降低离散卷积的计算复杂度,convolutions
模块利用transforms
模块进行高效计算(适用于长输入序列)。
变换
该部分列出了实现离散序列基本变换的方法。
快速傅立叶变换
sympy.discrete.transforms.fft(seq, dps=None)
在复数域中执行离散傅立叶变换(DFT)。
由于基数-2 FFT需要样本点数为 2 的幂,序列会自动向右填充零。
仅对短序列使用默认参数,因为表达式复杂度会随序列大小增加而增加。
参数:
seq:可迭代对象
应用DFT的序列。
dps:整数
指定精度的小数位数。
示例
>>> from sympy import fft, ifft
>>> fft([1, 2, 3, 4])
[10, -2 - 2*I, -2, -2 + 2*I]
>>> ifft(_)
[1, 2, 3, 4]
>>> ifft([1, 2, 3, 4])
[5/2, -1/2 + I/2, -1/2, -1/2 - I/2]
>>> fft(_)
[1, 2, 3, 4]
>>> ifft([1, 7, 3, 4], dps=15)
[3.75, -0.5 - 0.75*I, -1.75, -0.5 + 0.75*I]
>>> fft(_)
[1.0, 7.0, 3.0, 4.0]
参考
[R178]
en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm
[R179]
mathworld.wolfram.com/FastFourierTransform.html
sympy.discrete.transforms.ifft(seq, dps=None)
在复数域中执行离散傅立叶变换(DFT)。
由于基数-2 FFT需要样本点数为 2 的幂,序列会自动向右填充零。
仅对短序列使用默认参数,因为表达式复杂度会随序列大小增加而增加。
参数:
seq:可迭代对象
应用DFT的序列。
dps:整数
指定精度的小数位数。
示例
>>> from sympy import fft, ifft
>>> fft([1, 2, 3, 4])
[10, -2 - 2*I, -2, -2 + 2*I]
>>> ifft(_)
[1, 2, 3, 4]
>>> ifft([1, 2, 3, 4])
[5/2, -1/2 + I/2, -1/2, -1/2 - I/2]
>>> fft(_)
[1, 2, 3, 4]
>>> ifft([1, 7, 3, 4], dps=15)
[3.75, -0.5 - 0.75*I, -1.75, -0.5 + 0.75*I]
>>> fft(_)
[1.0, 7.0, 3.0, 4.0]
参考
[R180]
en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm
[R181]
mathworld.wolfram.com/FastFourierTransform.html
数论变换
sympy.discrete.transforms.ntt(seq, prime)
执行数论变换(NTT),专门针对素数(p)的商环(Z/pZ)上的离散傅立叶变换(DFT),而不是复数(C)。
由于基数-2 NTT需要样本点数为 2 的幂,序列会自动向右填充零。
参数:
seq:可迭代对象
应用DFT的序列。
prime : 整数
用于在序列上执行 NTT 的形式为((m 2^k + 1))的素数模数。
示例
>>> from sympy import ntt, intt
>>> ntt([1, 2, 3, 4], prime=3*2**8 + 1)
[10, 643, 767, 122]
>>> intt(_, 3*2**8 + 1)
[1, 2, 3, 4]
>>> intt([1, 2, 3, 4], prime=3*2**8 + 1)
[387, 415, 384, 353]
>>> ntt(_, prime=3*2**8 + 1)
[1, 2, 3, 4]
参考文献
[R182]
www.apfloat.org/ntt.html
[R183]
mathworld.wolfram.com/NumberTheoreticTransform.html
[R184]
[en.wikipedia.org/wiki/Discrete_Fourier_transform_(general%29
](https://en.wikipedia.org/wiki/Discrete_Fourier_transform_(general))
sympy.discrete.transforms.intt(seq, prime)
执行 Number Theoretic Transform(NTT),它专门用于素数(p)而不是复数(C)的环(Z/pZ)上的离散傅里叶变换(DFT)。
序列会自动在右侧填充零,因为基数-2 NTT要求样本点数为 2 的幂。
参数:
seq : iterable
应用 DFT 的序列。
prime : 整数
用于在序列上执行 NTT 的形式为((m 2^k + 1))的素数模数。
示例
>>> from sympy import ntt, intt
>>> ntt([1, 2, 3, 4], prime=3*2**8 + 1)
[10, 643, 767, 122]
>>> intt(_, 3*2**8 + 1)
[1, 2, 3, 4]
>>> intt([1, 2, 3, 4], prime=3*2**8 + 1)
[387, 415, 384, 353]
>>> ntt(_, prime=3*2**8 + 1)
[1, 2, 3, 4]
参考文献
[R185]
www.apfloat.org/ntt.html
[R186]
mathworld.wolfram.com/NumberTheoreticTransform.html
[R187]
[en.wikipedia.org/wiki/Discrete_Fourier_transform_(general%29
](https://en.wikipedia.org/wiki/Discrete_Fourier_transform_(general))
快速 Walsh-Hadamard 变换
sympy.discrete.transforms.fwht(seq)
执行 Walsh Hadamard Transform(WHT),并使用 Hadamard 排序序列。
序列会自动在右侧填充零,因为基数-2 FWHT要求样本点数为 2 的幂。
参数:
seq : iterable
应用 WHT 的序列。
示例
>>> from sympy import fwht, ifwht
>>> fwht([4, 2, 2, 0, 0, 2, -2, 0])
[8, 0, 8, 0, 8, 8, 0, 0]
>>> ifwht(_)
[4, 2, 2, 0, 0, 2, -2, 0]
>>> ifwht([19, -1, 11, -9, -7, 13, -15, 5])
[2, 0, 4, 0, 3, 10, 0, 0]
>>> fwht(_)
[19, -1, 11, -9, -7, 13, -15, 5]
参考文献
[R188]
en.wikipedia.org/wiki/Hadamard_transform
[R189]
en.wikipedia.org/wiki/Fast_Walsh%E2%80%93Hadamard_transform
sympy.discrete.transforms.ifwht(seq)
执行 Walsh Hadamard Transform(WHT),并使用 Hadamard 排序序列。
序列会自动在右侧填充零,因为基数-2 FWHT要求样本点数为 2 的幂。
参数:
seq : iterable
应用 WHT 的序列。
示例
>>> from sympy import fwht, ifwht
>>> fwht([4, 2, 2, 0, 0, 2, -2, 0])
[8, 0, 8, 0, 8, 8, 0, 0]
>>> ifwht(_)
[4, 2, 2, 0, 0, 2, -2, 0]
>>> ifwht([19, -1, 11, -9, -7, 13, -15, 5])
[2, 0, 4, 0, 3, 10, 0, 0]
>>> fwht(_)
[19, -1, 11, -9, -7, 13, -15, 5]
参考文献
[R190]
en.wikipedia.org/wiki/Hadamard_transform
[R191]
en.wikipedia.org/wiki/Fast_Walsh%E2%80%93Hadamard_transform
Möbius 变换
sympy.discrete.transforms.mobius_transform(seq, subset=True)
对子集格上的索引执行 Möbius 变换。
每个参数的索引,被视为位串,对应于有限集的子集。
序列会被自动用零填充到右边,因为基于位掩码(索引)的子集/超集的定义要求序列的大小必须是 2 的幂。
参数:
seq:可迭代对象
要应用 Mobius 变换的序列。
subset:布尔类型
通过枚举给定集合的子集或超集来确定是否应用 Mobius 变换。
示例
>>> from sympy import symbols
>>> from sympy import mobius_transform, inverse_mobius_transform
>>> x, y, z = symbols('x y z')
>>> mobius_transform([x, y, z])
[x, x + y, x + z, x + y + z]
>>> inverse_mobius_transform(_)
[x, y, z, 0]
>>> mobius_transform([x, y, z], subset=False)
[x + y + z, y, z, 0]
>>> inverse_mobius_transform(_, subset=False)
[x, y, z, 0]
>>> mobius_transform([1, 2, 3, 4])
[1, 3, 4, 10]
>>> inverse_mobius_transform(_)
[1, 2, 3, 4]
>>> mobius_transform([1, 2, 3, 4], subset=False)
[10, 6, 7, 4]
>>> inverse_mobius_transform(_, subset=False)
[1, 2, 3, 4]
参考文献
[R192]
en.wikipedia.org/wiki/M%C3%B6bius_inversion_formula
[R193]
people.csail.mit.edu/rrw/presentations/subset-conv.pdf
[R194]
arxiv.org/pdf/1211.0189.pdf
sympy.discrete.transforms.inverse_mobius_transform(seq, subset=True)
对序列的子集格上的指数进行 Mobius 变换。
每个参数的索引被视为位串,对应于有限集合的子集。
序列会被自动用零填充到右边,因为基于位掩码(索引)的子集/超集的定义要求序列的大小必须是 2 的幂。
参数:
seq:可迭代对象
要应用 Mobius 变换的序列。
subset:布尔类型
通过枚举给定集合的子集或超集来确定是否应用 Mobius 变换。
示例
>>> from sympy import symbols
>>> from sympy import mobius_transform, inverse_mobius_transform
>>> x, y, z = symbols('x y z')
>>> mobius_transform([x, y, z])
[x, x + y, x + z, x + y + z]
>>> inverse_mobius_transform(_)
[x, y, z, 0]
>>> mobius_transform([x, y, z], subset=False)
[x + y + z, y, z, 0]
>>> inverse_mobius_transform(_, subset=False)
[x, y, z, 0]
>>> mobius_transform([1, 2, 3, 4])
[1, 3, 4, 10]
>>> inverse_mobius_transform(_)
[1, 2, 3, 4]
>>> mobius_transform([1, 2, 3, 4], subset=False)
[10, 6, 7, 4]
>>> inverse_mobius_transform(_, subset=False)
[1, 2, 3, 4]
参考文献
[R195]
en.wikipedia.org/wiki/M%C3%B6bius_inversion_formula
[R196]
people.csail.mit.edu/rrw/presentations/subset-conv.pdf
[R197]
arxiv.org/pdf/1211.0189.pdf
## 卷积
此部分列出了用于离散序列基本卷积的方法。
卷积
这是一种计算离散序列卷积的通用方法,内部调用 convolution_fft
, convolution_ntt
, convolution_fwht
或 convolution_subset
中的一种方法。
sympy.discrete.convolutions.convolution(a, b, cycle=0, dps=None, prime=None, dyadic=None, subset=None)
使用提示来执行所需卷积类型的卷积。
在 dps
, prime
, dyadic
, subset
参数中,应明确指定一种以识别卷积类型,可选择地指定参数 cycle
。
对于默认参数,使用 FFT 执行线性卷积。
参数:
a, b:可迭代对象
要进行卷积的序列。
cycle:整数
指定进行循环卷积的长度。
dps:整数
指定在序列上执行 FFT 时的精度所需的小数位数。
prime:整数
用于在序列上执行 NTT 的形式为 ((m 2^k + 1)) 的素数模数。
dyadic:布尔类型
将卷积类型标识为二元(按位异或)卷积,使用 FWHT 执行。
subset:布尔类型
将卷积类型标识为子集卷积。
示例
>>> from sympy import convolution, symbols, S, I
>>> u, v, w, x, y, z = symbols('u v w x y z')
>>> convolution([1 + 2*I, 4 + 3*I], [S(5)/4, 6], dps=3)
[1.25 + 2.5*I, 11.0 + 15.8*I, 24.0 + 18.0*I]
>>> convolution([1, 2, 3], [4, 5, 6], cycle=3)
[31, 31, 28]
>>> convolution([111, 777], [888, 444], prime=19*2**10 + 1)
[1283, 19351, 14219]
>>> convolution([111, 777], [888, 444], prime=19*2**10 + 1, cycle=2)
[15502, 19351]
>>> convolution([u, v], [x, y, z], dyadic=True)
[u*x + v*y, u*y + v*x, u*z, v*z]
>>> convolution([u, v], [x, y, z], dyadic=True, cycle=2)
[u*x + u*z + v*y, u*y + v*x + v*z]
>>> convolution([u, v, w], [x, y, z], subset=True)
[u*x, u*y + v*x, u*z + w*x, v*z + w*y]
>>> convolution([u, v, w], [x, y, z], subset=True, cycle=3)
[u*x + v*z + w*y, u*y + v*x, u*z + w*x]
使用快速傅立叶变换进行卷积的序列。
sympy.discrete.convolutions.convolution_fft(a, b, dps=None)
使用快速傅立叶变换执行线性卷积。
参数:
a, b:可迭代对象
进行卷积的序列。
dps:整数
指定精度的十进制数字位数。
示例
>>> from sympy import S, I
>>> from sympy.discrete.convolutions import convolution_fft
>>> convolution_fft([2, 3], [4, 5])
[8, 22, 15]
>>> convolution_fft([2, 5], [6, 7, 3])
[12, 44, 41, 15]
>>> convolution_fft([1 + 2*I, 4 + 3*I], [S(5)/4, 6])
[5/4 + 5*I/2, 11 + 63*I/4, 24 + 18*I]
参考文献
[R198]
en.wikipedia.org/wiki/Convolution_theorem
[R199]
[en.wikipedia.org/wiki/Discrete_Fourier_transform_(general%29
](https://en.wikipedia.org/wiki/Discrete_Fourier_transform_(general))
使用数论变换执行卷积
sympy.discrete.convolutions.convolution_ntt(a, b, prime)
使用数论变换执行线性卷积。
参数:
a, b:可迭代对象
进行卷积的序列。
prime:整数
用于在序列上执行 NTT 的形式为((m 2^k + 1))的素数模数。
示例
>>> from sympy.discrete.convolutions import convolution_ntt
>>> convolution_ntt([2, 3], [4, 5], prime=19*2**10 + 1)
[8, 22, 15]
>>> convolution_ntt([2, 5], [6, 7, 3], prime=19*2**10 + 1)
[12, 44, 41, 15]
>>> convolution_ntt([333, 555], [222, 666], prime=19*2**10 + 1)
[15555, 14219, 19404]
参考文献
[R200]
en.wikipedia.org/wiki/Convolution_theorem
[R201]
[en.wikipedia.org/wiki/Discrete_Fourier_transform_(general%29
](https://en.wikipedia.org/wiki/Discrete_Fourier_transform_(general))
使用快速沃尔什-哈达玛变换执行子集卷积。
sympy.discrete.convolutions.convolution_fwht(a, b)
使用快速沃尔什-哈达玛变换进行二进制(按位异或)卷积。
该卷积会自动用零填充到右侧,因为基于位掩码的 Radix-2 FWHT要求采样点数为 2 的幂。
参数:
a, b:可迭代对象
进行卷积的序列。
示例
>>> from sympy import symbols, S, I
>>> from sympy.discrete.convolutions import convolution_fwht
>>> u, v, x, y = symbols('u v x y')
>>> convolution_fwht([u, v], [x, y])
[u*x + v*y, u*y + v*x]
>>> convolution_fwht([2, 3], [4, 5])
[23, 22]
>>> convolution_fwht([2, 5 + 4*I, 7], [6*I, 7, 3 + 4*I])
[56 + 68*I, -10 + 30*I, 6 + 50*I, 48 + 32*I]
>>> convolution_fwht([S(33)/7, S(55)/6, S(7)/4], [S(2)/3, 5])
[2057/42, 1870/63, 7/6, 35/4]
参考文献
[R202]
www.radioeng.cz/fulltexts/2002/02_03_40_42.pdf
[R203]
en.wikipedia.org/wiki/Hadamard_transform
子集卷积
sympy.discrete.convolutions.convolution_subset(a, b)
对给定序列执行子集卷积。
每个参数的索引,视为位字符串,对应于有限集合的子集。
该序列会自动用零填充到右侧,因为基于位掩码(索引)的子集定义要求序列大小为 2 的幂。
参数:
a, b:可迭代对象
进行卷积的序列。
示例
>>> from sympy import symbols, S
>>> from sympy.discrete.convolutions import convolution_subset
>>> u, v, x, y, z = symbols('u v x y z')
>>> convolution_subset([u, v], [x, y])
[u*x, u*y + v*x]
>>> convolution_subset([u, v, x], [y, z])
[u*y, u*z + v*y, x*y, x*z]
>>> convolution_subset([1, S(2)/3], [3, 4])
[3, 6]
>>> convolution_subset([1, 3, S(5)/7], [7])
[7, 21, 5, 0]
参考文献
[R204]
people.csail.mit.edu/rrw/presentations/subset-conv.pdf
覆盖乘积
sympy.discrete.convolutions.covering_product(a, b)
返回给定序列的覆盖乘积。
每个参数的索引,视为位字符串,对应于有限集合的子集。
给定序列的覆盖乘积是一个序列,其中包含给定序列的元素按相应索引的按位或分组后的乘积之和。
该序列会自动用零填充到右侧,因为基于位掩码(索引)的子集定义要求序列大小为 2 的幂。
参数:
a, b:可迭代对象
进行覆盖乘积的序列。
示例
>>> from sympy import symbols, S, I, covering_product
>>> u, v, x, y, z = symbols('u v x y z')
>>> covering_product([u, v], [x, y])
[u*x, u*y + v*x + v*y]
>>> covering_product([u, v, x], [y, z])
[u*y, u*z + v*y + v*z, x*y, x*z]
>>> covering_product([1, S(2)/3], [3, 4 + 5*I])
[3, 26/3 + 25*I/3]
>>> covering_product([1, 3, S(5)/7], [7, 8])
[7, 53, 5, 40/7]
参考文献
[R205]
people.csail.mit.edu/rrw/presentations/subset-conv.pdf
交集积
sympy.discrete.convolutions.intersecting_product(a, b)
返回给定序列的交集积。
每个参数的指数,被视为位字符串,对应于有限集的子集。
给定序列的交集积是包含给定序列元素按对应索引的位与(bitwise-AND)计算的乘积和的序列。
由于基于位掩码(索引)的子集定义要求序列的大小是 2 的幂次方,因此序列会自动在右侧填充零。
参数:
a, b : 可迭代对象
所需获取交集积的序列。
示例
>>> from sympy import symbols, S, I, intersecting_product
>>> u, v, x, y, z = symbols('u v x y z')
>>> intersecting_product([u, v], [x, y])
[u*x + u*y + v*x, v*y]
>>> intersecting_product([u, v, x], [y, z])
[u*y + u*z + v*y + x*y + x*z, v*z, 0, 0]
>>> intersecting_product([1, S(2)/3], [3, 4 + 5*I])
[9 + 5*I, 8/3 + 10*I/3]
>>> intersecting_product([1, 3, S(5)/7], [7, 8])
[327/7, 24, 0, 0]
参考文献
[R206]
people.csail.mit.edu/rrw/presentations/subset-conv.pdf
数值评估
原文:
docs.sympy.org/latest/modules/evalf.html
基础
精确的 SymPy 表达式可以使用 .evalf()
方法或 N()
函数转换为浮点数近似值(小数)。N(expr, <args>)
等效于 sympify(expr).evalf(<args>)
。
>>> from sympy import *
>>> N(sqrt(2)*pi)
4.44288293815837
>>> (sqrt(2)*pi).evalf()
4.44288293815837
默认情况下,数值评估的精度为 15 位小数。您可以选择将所需的精度(应为正整数)作为参数传递给 evalf
或 N
:
>>> N(sqrt(2)*pi, 5)
4.4429
>>> N(sqrt(2)*pi, 50)
4.4428829381583662470158809900606936986146216893757
支持复数:
>>> N(1/(pi + I), 20)
0.28902548222223624241 - 0.091999668350375232456*I
如果表达式包含符号或由于其他原因无法进行数值评估,则调用 .evalf()
或 N()
将返回原始表达式,或者在某些情况下返回部分评估的表达式。例如,当表达式是展开形式的多项式时,将评估系数:
>>> x = Symbol('x')
>>> (pi*x**2 + x/3).evalf()
3.14159265358979*x**2 + 0.333333333333333*x
您还可以使用标准 Python 函数 float()
、complex()
将 SymPy 表达式转换为常规 Python 数字:
>>> float(pi)
3.141592653589793
>>> complex(pi+E*I)
(3.141592653589793+2.718281828459045j)
如果使用这些函数,未能将表达式评估为显式数字(例如,如果表达式包含符号),则会引发异常。
本质上不存在上限精度限制。例如,以下命令计算了π/e 的前 100,000 位小数:
>>> N(pi/E, 100000)
...
这显示了π的第 999,951 至 1,000,000 位小数:
>>> str(N(pi, 10**6))[-50:]
'95678796130331164628399634646042209010610577945815'
高精度计算可能会很慢。建议(但完全可选)安装 gmpy(github.com/aleaxit/gmpy
),这将显著加快上述计算等的速度。
浮点数
SymPy 中的浮点数是 Float
类的实例。可以将 Float
作为第二个参数创建自定义精度:
>>> Float(0.1)
0.100000000000000
>>> Float(0.1, 10)
0.1000000000
>>> Float(0.125, 30)
0.125000000000000000000000000000
>>> Float(0.1, 30)
0.100000000000000005551115123126
正如最后一个示例所示,某些 Python 浮点数仅精确到约 15 位小数输入,而其他(例如分母为 2 的幂的浮点数,如 0.125 = 1/8)则是精确的。要从高精度小数创建 Float
,最好传递字符串、Rational
或 evalf
Rational
:
>>> Float('0.1', 30)
0.100000000000000000000000000000
>>> Float(Rational(1, 10), 30)
0.100000000000000000000000000000
>>> Rational(1, 10).evalf(30)
0.100000000000000000000000000000
数的精度决定了 1)在与该数进行算术运算时使用的精度,以及 2)打印该数时显示的位数。当两个不同精度的数一起进行算术运算时,结果将使用较高的精度。0.1 +/- 0.001 和 3.1415 +/- 0.0001 的乘积的不确定性约为 0.003,但显示了 5 位有效数字。
>>> Float(0.1, 3)*Float(3.1415, 5)
0.31417
因此,显示的精度不应用作错误传播或重要性算术的模型;相反,该方案用于确保数值算法的稳定性。
N
和 evalf
可用于更改现有浮点数的精度:
>>> N(3.5)
3.50000000000000
>>> N(3.5, 5)
3.5000
>>> N(3.5, 30)
3.50000000000000000000000000000
精度和错误处理
当输入到 N
或 evalf
的是复杂表达式时,数值误差传播成为一个问题。例如,考虑第 100 个斐波那契数和卓越但不精确的近似 (\varphi^{100} / \sqrt{5}),其中 (\varphi) 是黄金比例。使用普通浮点算术,将这些数字相减会错误地导致完全的取消:
>>> a, b = GoldenRatio**1000/sqrt(5), fibonacci(1000)
>>> float(a)
4.34665576869e+208
>>> float(b)
4.34665576869e+208
>>> float(a) - float(b)
0.0
N
和 evalf
会跟踪错误并自动增加内部使用的精度,以获得正确的结果:
>>> N(fibonacci(100) - GoldenRatio**100/sqrt(5))
-5.64613129282185e-22
不幸的是,数值评估无法区分一个恰好为零的表达式和一个仅仅非常小的表达式。因此,默认情况下工作精度被限制在大约 100 位数字。如果我们尝试使用第 1000 个斐波那契数,会发生以下情况:
>>> N(fibonacci(1000) - (GoldenRatio)**1000/sqrt(5))
0.e+85
返回数字的位数不足表明 N
未能达到完全的精确度。结果表明表达式的大小为 10⁸⁴ 以下,但这并不是一个特别好的答案。要强制使用更高的工作精度,可以使用 maxn
关键字参数:
>>> N(fibonacci(1000) - (GoldenRatio)**1000/sqrt(5), maxn=500)
-4.60123853010113e-210
通常情况下,maxn
可以设置得非常高(数千位数字),但请注意,这可能在极端情况下导致显著的减速。或者,可以将 strict=True
选项设置为强制引发异常,而不是在请求的精度不足时静默返回值:
>>> N(fibonacci(1000) - (GoldenRatio)**1000/sqrt(5), strict=True)
Traceback (most recent call last):
...
PrecisionExhausted: Failed to distinguish the expression:-sqrt(5)*GoldenRatio**1000/5 + 43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875from zero. Try simplifying the input, using chop=True, or providing a higher maxn for evalf
如果我们添加一个项,使得斐波那契逼近变得精确(Binet 公式的完整形式),我们得到一个恰好为零的表达式,但 N
不知道这一点:
>>> f = fibonacci(100) - (GoldenRatio**100 - (GoldenRatio-1)**100)/sqrt(5)
>>> N(f)
0.e-104
>>> N(f, maxn=1000)
0.e-1336
在已知会发生这种取消的情况下,chop
选项非常有用。这基本上会将数字的实部或虚部中非常小的数替换为精确的零:
>>> N(f, chop=True)
0
>>> N(3 + I*f, chop=True)
3.00000000000000
在希望去除无意义数字的情况下,重新评估或使用 round 方法非常有用:
>>> Float('.1', '')*Float('.12345', '')
0.012297
>>> ans = _
>>> N(ans, 1)
0.01
>>> ans.round(2)
0.01
如果处理的数值表达式不包含浮点数,可以以任意精度进行评估。要将结果舍入到给定小数的相对值,round 方法非常有用:
>>> v = 10*pi + cos(1)
>>> N(v)
31.9562288417661
>>> v.round(3)
31.956
总和与积分
总和(特别是无穷级数)和积分可以像常规闭合形式表达式一样使用,并支持任意精度评估:
>>> var('n x')
(n, x)
>>> Sum(1/n**n, (n, 1, oo)).evalf()
1.29128599706266
>>> Integral(x**(-x), (x, 0, 1)).evalf()
1.29128599706266
>>> Sum(1/n**n, (n, 1, oo)).evalf(50)
1.2912859970626635404072825905956005414986193682745
>>> Integral(x**(-x), (x, 0, 1)).evalf(50)
1.2912859970626635404072825905956005414986193682745
>>> (Integral(exp(-x**2), (x, -oo, oo)) ** 2).evalf(30)
3.14159265358979323846264338328
默认情况下,使用双曲正弦积分算法来评估积分。对于平滑的被积函数(甚至是具有端点奇异性的积分),此算法非常高效和稳健,但可能在高度振荡或中间间断的积分中遇到困难。在许多情况下,evalf
/ N
将正确估计误差。对于以下积分,结果是准确的,但只精确到四位数:
>>> f = abs(sin(x))
>>> Integral(abs(sin(x)), (x, 0, 4)).evalf()
2.346
最好将这个积分拆分成两个部分:
>>> (Integral(f, (x, 0, pi)) + Integral(f, (x, pi, 4))).evalf()
2.34635637913639
类似的例子是以下振荡积分:
>>> Integral(sin(x)/x**2, (x, 1, oo)).evalf(maxn=20)
0.5
可以通过告知 evalf
或 N
使用振荡积分算法来更有效地处理它:
>>> Integral(sin(x)/x**2, (x, 1, oo)).evalf(quad='osc')
0.504067061906928
>>> Integral(sin(x)/x**2, (x, 1, oo)).evalf(20, quad='osc')
0.50406706190692837199
振荡积分需要包含形如 cos(ax+b) 或 sin(ax+b) 因子的被积函数。请注意,许多其他振荡积分可以通过变量变换转换为此形式:
>>> init_printing(use_unicode=False)
>>> intgrl = Integral(sin(1/x), (x, 0, 1)).transform(x, 1/x)
>>> intgrloo/|| sin(x)| ------ dx| 2| x|
/
1
>>> N(intgrl, quad='osc')
0.504067061906928
如果无穷级数收敛速度足够快,直接求和。否则,使用外推方法(通常是欧拉-麦克劳林公式但也包括理查森外推)加速收敛。这允许对缓慢收敛级数进行高精度评估:
>>> var('k')
k
>>> Sum(1/k**2, (k, 1, oo)).evalf()
1.64493406684823
>>> zeta(2).evalf()
1.64493406684823
>>> Sum(1/k-log(1+1/k), (k, 1, oo)).evalf()
0.577215664901533
>>> Sum(1/k-log(1+1/k), (k, 1, oo)).evalf(50)
0.57721566490153286060651209008240243104215933593992
>>> EulerGamma.evalf(50)
0.57721566490153286060651209008240243104215933593992
欧拉-麦克劳林公式还用于有限级数,允许快速近似而不需评估所有项:
>>> Sum(1/k, (k, 10000000, 20000000)).evalf()
0.693147255559946
请注意,evalf
做出了不总是最优的一些假设。为了对数值求和进行精细调整,手动使用 Sum.euler_maclaurin
方法可能是值得的。
为了有理超几何级数(其中项是多项式、幂、阶乘、二项式系数等的乘积)采用了特殊优化。N
/evalf
类型的级数可以非常快速地高精度求和。例如,这个拉马努金公式可以用简单的命令在几分之一秒内求和到 10,000 位数:
>>> f = factorial
>>> n = Symbol('n', integer=True)
>>> R = 9801/sqrt(8)/Sum(f(4*n)*(1103+26390*n)/f(n)**4/396**(4*n),
... (n, 0, oo))
>>> N(R, 10000)
3.141592653589793238462643383279502884197169399375105820974944592307816406286208
99862803482534211706798214808651328230664709384460955058223172535940812848111745
02841027019385211055596446229489549303819644288109756659334461284756482337867831
...
数值简化
函数 nsimplify
尝试找到一个数值上等于给定输入的公式。此功能可用于猜测精确浮点输入的确切公式,或者猜测复杂符号输入的简化公式。nsimplify
使用的算法能够识别简单分数、简单代数表达式、给定常数的线性组合以及任何前述内容的某些基本函数变换。
可选地,nsimplify
可以传入常数列表(例如 pi)和最小数值容差。以下是一些基本示例:
>>> nsimplify(0.1)
1/10
>>> nsimplify(6.28, [pi], tolerance=0.01)
2*pi
>>> nsimplify(pi, tolerance=0.01)
22/7
>>> nsimplify(pi, tolerance=0.001)
355
---
113
>>> nsimplify(0.33333, tolerance=1e-4)
1/3
>>> nsimplify(2.0**(1/3.), tolerance=0.001)
635
---
504
>>> nsimplify(2.0**(1/3.), tolerance=0.001, full=True)
3 ___
\/ 2
这里是几个更高级的示例:
>>> nsimplify(Float('0.130198866629986772369127970337',30), [pi, E])1
----------
5*pi
---- + 2*e7
>>> nsimplify(cos(atan('1/3')))____
3*\/ 10
--------10
>>> nsimplify(4/(1+sqrt(5)), [GoldenRatio])
-2 + 2*GoldenRatio
>>> nsimplify(2 + exp(2*atan('1/4')*I))
49 8*I
-- + ---
17 17
>>> nsimplify((1/(exp(3*pi*I/5)+1)))___________/ ___
1 / \/ 5 1
- - I* / ----- + -
2 \/ 10 4
>>> nsimplify(I**I, [pi])-pi----2
e
>>> n = Symbol('n')
>>> nsimplify(Sum(1/n**2, (n, 1, oo)), [pi])2
pi
---6
>>> nsimplify(gamma('1/4')*gamma('3/4'), [pi])___
\/ 2 *pi
数值计算
原文链接:
docs.sympy.org/latest/modules/numeric-computation.html
像 SymPy 这样的符号计算代数系统有助于构建和操作数学表达式。但是,当需要对数值数据进行评估时,符号系统的性能通常较差。
幸运的是,SymPy 提供了许多易于使用的钩子,可以连接到其他数值系统,允许您在 SymPy 中创建数学表达式,然后将其传送到您选择的数值系统。本页记录了许多可用选项,包括math
库、流行的数组计算包numpy
、在 Fortran 或 C 中生成代码以及使用数组编译器Aesara
。
Subs/evalf
Subs 是最慢但最简单的选项。它以 SymPy 的速度运行。.subs(...).evalf()
方法可以用数值值替换符号值,然后在 SymPy 中评估结果。
>>> from sympy import *
>>> from sympy.abc import x
>>> expr = sin(x)/x
>>> expr.evalf(subs={x: 3.14})
0.000507214304613640
这种方法速度较慢。仅在性能不是问题时,才应在生产中使用此方法。你可以预期.subs
耗时数十微秒。在原型设计阶段或者只需查看值时,这可能很有用。
Lambdify
lambdify
函数将 SymPy 表达式转换为 Python 函数,利用各种数值库。其用法如下:
>>> from sympy import *
>>> from sympy.abc import x
>>> expr = sin(x)/x
>>> f = lambdify(x, expr)
>>> f(3.14)
0.000507214304614
在这里,lambdify 创建一个计算f(x) = sin(x)/x
的函数。默认情况下,lambdify 依赖于math
标准库中的实现。这种数值评估大约需要数百纳秒,比.subs
方法快大约两个数量级。这是 SymPy 和原始 Python 之间的速度差异。
Lambdify 可以利用多种数值后端。默认情况下使用math
库。但它也支持mpmath
和最显著的是numpy
。使用numpy
库可以让生成的函数访问由编译的 C 代码支持的强大的矢量化 ufuncs。
>>> from sympy import *
>>> from sympy.abc import x
>>> expr = sin(x)/x
>>> f = lambdify(x, expr, "numpy")
>>> import numpy
>>> data = numpy.linspace(1, 10, 10000)
>>> f(data)
[ 0.84147098 0.84119981 0.84092844 ... -0.05426074 -0.05433146-0.05440211]
如果你有基于数组的数据,这可能会显著加快速度,每个元素大约在 10 纳秒左右。不幸的是,NumPy 会产生一些启动时间,并引入几微秒的额外开销。
CuPy 是一个与 NumPy 兼容的数组库,主要运行在 CUDA 上,但也越来越多地支持其他 GPU 制造商。在许多情况下,它可以作为 numpy 的即插即用替代品。
>>> f = lambdify(x, expr, "cupy")
>>> import cupy as cp
>>> data = cp.linspace(1, 10, 10000)
>>> y = f(data) # perform the computation
>>> cp.asnumpy(y) # explicitly copy from GPU to CPU / numpy array
[ 0.84147098 0.84119981 0.84092844 ... -0.05426074 -0.05433146-0.05440211]
JAX 是 CuPy 的类似替代方案,通过即时编译到 XLA 提供 GPU 和 TPU 加速。在某些情况下,它也可以作为 numpy 的即插即用替代品。
>>> f = lambdify(x, expr, "jax")
>>> import jax.numpy as jnp
>>> data = jnp.linspace(1, 10, 10000)
>>> y = f(data) # perform the computation
>>> numpy.asarray(y) # explicitly copy to CPU / numpy array
array([ 0.84147096, 0.8411998 , 0.84092844, ..., -0.05426079,-0.05433151, -0.05440211], dtype=float32)
uFuncify
内联代码(autowrap
)模块包含一些能够帮助进行高效计算的方法。
-
autowrap 方法用于编译由 codegen 模块生成的代码,并将二进制包装供 Python 使用。
-
binary_function 方法自动化了将 SymPy 表达式自动包装并附加到
Function
对象的步骤。 -
ufuncify 生成一个二元函数,支持在 numpy 数组上进行广播,使用不同的后端比
subs/evalf
和lambdify
更快。
所有上述内容的 API 参考在这里列出:sympy.utilities.autowrap()
。
Aesara
SymPy 与Aesara有着紧密的连接,是一个数学数组编译器。SymPy 表达式可以轻松转换为 Aesara 图,然后使用 Aesara 编译器链进行编译。
>>> from sympy import *
>>> from sympy.abc import x
>>> expr = sin(x)/x
>>> from sympy.printing.aesaracode import aesara_function
>>> f = aesara_function([x], [expr])
如果希望进行数组广播或者类型处理,Aesara 需要额外的信息。
>>> f = aesara_function([x], [expr], dims={x: 1}, dtypes={x: 'float64'})
Aesara 比 SymPy 的 C/Fortran 代码打印机有一个更复杂的代码生成系统。除其他外,它处理常见的子表达式,并编译到 GPU 上。Aesara 还支持 SymPy 的 Matrix 和 Matrix Expression 对象。
所以我应该使用哪一个?
这里的选项按从最慢和最少依赖到最快和最多依赖的顺序列出。例如,如果安装了 Aesara,则通常是最佳选择。如果没有安装 Aesara 但安装了f2py
,则应使用ufuncify
。如果您一直使用 numpy 模块使用 lambdify,并且有 GPU,那么 CuPy 和 JAX 可以提供显著的加速效果而几乎没有额外工作。
工具 | 速度 | 特性 | 依赖项 |
---|---|---|---|
subs/evalf | 50us | 简单 | None |
lambdify | 1us | 标量函数 | math |
lambdify-numpy | 10ns | 向量函数 | numpy |
ufuncify | 10ns | 复杂向量表达式 | f2py, Cython |
lambdify-cupy | 10ns | GPU 上的向量函数 | cupy |
lambdify-jax | 10ns | CPU、GPU 和 TPU 上的向量函数 | jax |
Aesara | 10ns | 多输出,CSE,GPU | Aesara |
项重写
原文链接:
docs.sympy.org/latest/modules/rewriting.html
术语重写是一种非常一般化的功能类,用于将一种类型的表达式转换为不同种类的表达式。例如,展开、组合和转换表达式适用于术语重写,同时还可以包括简化例程。目前 SymPy 具有多个函数和基本内置方法,用于执行各种类型的重写。
展开
最简单的重写规则是将表达式展开成稀疏形式。展开有几种类型,包括复值表达式的展开,乘积和幂的算术展开,以及将函数展开为更一般的函数。以下列出了所有当前可用的展开规则。
对涉及乘积和幂的算术表达式的展开:
>>> from sympy import *
>>> x, y, z = symbols('x,y,z')
>>> ((x + y)*(x - y)).expand(basic=True)
x**2 - y**2
>>> ((x + y + z)**2).expand(basic=True)
x**2 + 2*x*y + 2*x*z + y**2 + 2*y*z + z**2
默认情况下,在 expand()
中进行算术展开,因此可以省略关键字 basic
。但是,如果使用下面描述的规则,可以设置 basic=False
来避免这种类型的展开。这样可以完全控制表达式的处理方式。
另一种展开规则是将复值表达式展开并将其放入正常形式。可以使用 complex
关键字来实现这一点。请注意,它将始终执行算术展开以获得所需的正常形式:
>>> (x + I*y).expand(complex=True)
re(x) + I*re(y) + I*im(x) - im(y)
>>> sin(x + I*y).expand(complex=True)
sin(re(x) - im(y))*cosh(re(y) + im(x)) + I*cos(re(x) - im(y))*sinh(re(y) + im(x))
还要注意,可以使用 as_real_imag()
方法来获得相同的行为。但是,它会返回一个包含实部在第一位和虚部在其他位置的元组。可以通过使用 collect
函数进行两步处理来完成这个过程:
>>> (x + I*y).as_real_imag()
(re(x) - im(y), re(y) + im(x))
>>> collect((x + I*y).expand(complex=True), I, evaluate=False)
{1: re(x) - im(y), I: re(y) + im(x)}
还可以按不同种类的表达式展开表达式。这是一种非常一般化的展开类型,通常会使用 rewrite()
来进行特定类型的重写:
>>> GoldenRatio.expand(func=True)
1/2 + sqrt(5)/2
公共子表达式检测和收集
在评估大型表达式之前,通常有助于识别公共子表达式,收集它们并一次性评估它们。这在 cse
函数中实现。例如:
>>> from sympy import cse, sqrt, sin, pprint
>>> from sympy.abc import x>>> pprint(cse(sqrt(sin(x))), use_unicode=True)
⎛ ⎡ ________⎤⎞
⎝[], ⎣╲╱ sin(x) ⎦⎠>>> pprint(cse(sqrt(sin(x)+5)*sqrt(sin(x)+4)), use_unicode=True)
⎛ ⎡ ________ ________⎤⎞
⎝[(x₀, sin(x))], ⎣╲╱ x₀ + 4 ⋅╲╱ x₀ + 5 ⎦⎠>>> pprint(cse(sqrt(sin(x+1) + 5 + cos(y))*sqrt(sin(x+1) + 4 + cos(y))),
... use_unicode=True)
⎛ ⎡ ________ ________⎤⎞
⎝[(x₀, sin(x + 1) + cos(y))], ⎣╲╱ x₀ + 4 ⋅╲╱ x₀ + 5 ⎦⎠>>> pprint(cse((x-y)*(z-y) + sqrt((x-y)*(z-y))), use_unicode=True)
⎛ ⎡ ____ ⎤⎞
⎝[(x₀, (x - y)⋅(-y + z))], ⎣╲╱ x₀ + x₀⎦⎠
可以在 optimizations
可选参数中传递公共子表达式消除之前和之后要执行的优化。可以通过传递 optimizations='basic'
应用一组预定义的基本优化:
>>> pprint(cse((x-y)*(z-y) + sqrt((x-y)*(z-y)), optimizations='basic'),
... use_unicode=True)
⎛ ⎡ ____ ⎤⎞
⎝[(x₀, -(x - y)⋅(y - z))], ⎣╲╱ x₀ + x₀⎦⎠
然而,对于大型表达式来说,这些优化可能会非常缓慢。此外,如果速度是一个问题,可以传递选项 order='none'
。然后,术语的顺序将取决于哈希算法的实现,但速度将得到极大的改善。
更多信息:
sympy.simplify.cse_main.cse(exprs, symbols=None, optimizations=None, postprocess=None, order='canonical', ignore=(), list=True)
对表达式进行公共子表达式消除。
参数:
exprs:SymPy 表达式列表,或单个 SymPy 表达式
待减少的表达式。
symbols:产生唯一符号的无限迭代器
用于标记被提取的共同子表达式的符号。
numbered_symbols
生成器非常有用。默认情况下是形式为 "x0"、"x1" 等的符号流。这必须是一个无限迭代器。
optimizations:(可调用,可调用)对列表
外部优化函数的(预处理器,后处理器)对。可选地,可以传递 'basic' 以获得一组预定义的基本优化。这些“basic”优化在旧实现中默认使用,但在较大的表达式上可能非常慢。现在,默认情况下不进行预处理或后处理优化。
postprocess:接受 cse 的两个返回值的函数和
返回从 cse 中的期望输出形式,例如如果您希望替换反转,则函数可能是以下 lambda:lambda r, e: return reversed(r), e
order:字符串,'none' 或 'canonical'
处理 Mul 和 Add 参数的顺序。如果设置为 'canonical',参数将按照规范顺序排列。如果设置为 'none',排序将更快但依赖于表达式哈希,因此是机器相关和可变的。对于速度是关键问题的大型表达式,请使用 order='none'。
ignore:符号的可迭代集合
包含任何
ignore
中符号的替换将被忽略。
list:布尔值,(默认为 True)
返回表达式列表或者具有与输入相同类型的输出(当为 False 时)。
返回:
replacements:(符号,表达式)对列表
所有被替换的共同子表达式。此列表中较早的子表达式可能会出现在此列表较晚的子表达式中。
reduced_exprs:SymPy 表达式的列表
具有上述所有替换的减少表达式。
示例
>>> from sympy import cse, SparseMatrix
>>> from sympy.abc import x, y, z, w
>>> cse(((w + x + y + z)*(w + y + z))/(w + x)**3)
([(x0, y + z), (x1, w + x)], [(w + x0)*(x0 + x1)/x1**3])
使用递归替换的表达式列表:
>>> m = SparseMatrix([x + y, x + y + z])
>>> cse([(x+y)**2, x + y + z, y + z, x + z + y, m])
([(x0, x + y), (x1, x0 + z)], [x0**2, x1, y + z, x1, Matrix([
[x0],
[x1]])])
注意:输入矩阵的类型和可变性保留。
>>> isinstance(_[1][-1], SparseMatrix)
True
用户可能禁止包含特定符号的替换:
>>> cse([y**2*(x + 1), 3*y**2*(x + 1)], ignore=(y,))
([(x0, x + 1)], [x0*y**2, 3*x0*y**2])
即使只有一个表达式,默认情况下减少后的返回值也是一个列表。 list
标志保留输出中输入的类型:
>>> cse(x)
([], [x])
>>> cse(x, list=False)
([], x)
代码生成
原文:
docs.sympy.org/latest/reference/public/codegeneration/index.html
目录
- 代码生成
代码生成
原始文档:
docs.sympy.org/latest/modules/codegen.html
SymPy 的多个子模块允许直接从 SymPy 表达式生成可直接编译和执行的代码,支持多种不同的编程语言。此外,还有一些函数生成 Python 可导入的对象,能够高效地评估 SymPy 表达式。
我们将从简要介绍构成 SymPy 代码生成功能的组件开始。
介绍
有四个主要的抽象层次:
expression|
code printers|
code generators|
autowrap
sympy.utilities.autowrap
使用了 codegen,而 codegen 又使用了代码打印机。sympy.utilities.autowrap
实现了一步到位:在同一个 Python 进程中,它让您能够从 SymPy 表达式转换为数值函数。 Codegen 是实际的代码生成,即编译和以后使用,或者包含在某个更大的项目中。
代码打印机将 SymPy 对象翻译成实际的代码,如 abs(x) -> fabs(x)
(用于 C)。
在许多情况下,代码打印机并不输出最优的代码。例如,在 C 中,幂运算 x**2
输出为 pow(x, 2)
而不是 x*x
。其他优化(如数学简化)应该在代码打印机之前进行。
目前,在此链中自动应用 sympy.simplify.cse_main.cse()
尚未发生。理想情况下,它应该在代码生成级别或其上某处发生。
我们将逐级介绍以下内容。
以下三行将用于设置每个示例:
>>> from sympy import *
>>> init_printing(use_unicode=True)
>>> from sympy.abc import a, e, k, n, r, t, x, y, z, T, Z
>>> from sympy.abc import beta, omega, tau
>>> f, g = symbols('f, g', cls=Function)
代码打印机(sympy.printing)
这里是代码生成的核心;SymPy 的翻译实际上更像是 Python 的轻量级代码生成版本,而 Python (sympy.printing.pycode.pycode()
) 和 sympy.printing.lambdarepr.lambdarepr()
,支持许多库(如 NumPy),以及 Aesara (sympy.printing.aesaracode.aesara_function()
)。代码打印机是 SymPy 中其他打印机(如字符串打印机、美观打印机等)的特殊情况。
一个重要的区别是,代码打印机必须处理赋值(使用 sympy.codegen.ast.Assignment
对象)。这作为代码打印机及其 codegen
模块的构建块。以下是在 C 代码中使用 Assignment
的示例:
>>> from sympy.codegen.ast import Assignment
>>> print(ccode(Assignment(x, y + 1)))
x = y + 1;
这里是另一个打印 SymPy 表达式 C 版本的简单示例:
>>> expr = (Rational(-1, 2) * Z * k * (e**2) / r)
>>> expr2
-Z⋅e ⋅k
────────2⋅r
>>> ccode(expr)
-1.0/2.0*Z*pow(e, 2)*k/r
>>> from sympy.codegen.ast import real, float80
>>> ccode(expr, assign_to="E", type_aliases={real: float80})
E = -1.0L/2.0L*Z*powl(e, 2)*k/r;
要生成使用例如 C99 标准提供的一些数学函数的代码,我们需要从sympy.codegen.cfunctions
导入函数:
>>> from sympy.codegen.cfunctions import expm1
>>> ccode(expm1(x), standard='C99')
expm1(x)
Piecewise
表达式将转换为条件语句。如果提供了assign_to
变量,则创建一个 if 语句,否则使用三元运算符。请注意,如果Piecewise
缺少由(expr, True)
表示的默认项,则会引发错误。这是为了防止生成一个可能不会评估为任何内容的表达式。一个Piecewise
的用例:
>>> expr = Piecewise((x + 1, x > 0), (x, True))
>>> print(fcode(expr, tau))if (x > 0) thentau = x + 1elsetau = xend if
各种打印机通常也很好地支持Indexed
对象。使用contract=True
这些表达式将被转换为循环,而contract=False
则只会打印应该循环的赋值表达式:
>>> len_y = 5
>>> mat_1 = IndexedBase('mat_1', shape=(len_y,))
>>> mat_2 = IndexedBase('mat_2', shape=(len_y,))
>>> Dy = IndexedBase('Dy', shape=(len_y-1,))
>>> i = Idx('i', len_y-1)
>>> eq = Eq(Dy[i], (mat_1[i+1] - mat_1[i]) / (mat_2[i+1] - mat_2[i]))
>>> print(jscode(eq.rhs, assign_to=eq.lhs, contract=False))
Dy[i] = (mat_1[i + 1] - mat_1[i])/(mat_2[i + 1] - mat_2[i]);
>>> Res = IndexedBase('Res', shape=(len_y,))
>>> j = Idx('j', len_y)
>>> eq = Eq(Res[j], mat_1[j]*mat_2[j])
>>> print(jscode(eq.rhs, assign_to=eq.lhs, contract=True))
for (var j=0; j<5; j++){Res[j] = 0;
}
for (var j=0; j<5; j++){for (var j=0; j<5; j++){Res[j] = Res[j] + mat_1[j]*mat_2[j];}
}
>>> print(jscode(eq.rhs, assign_to=eq.lhs, contract=False))
Res[j] = mat_1[j]*mat_2[j];
可以通过将“type”:“function”字典传递给user_functions
关键字来为某些类型定义自定义打印。或者,字典值可以是元组列表,即[(argument_test, cfunction_string)]
。这可以用于调用自定义 Octave 函数:
>>> custom_functions = {
... "f": "existing_octave_fcn",
... "g": [(lambda x: x.is_Matrix, "my_mat_fcn"),
... (lambda x: not x.is_Matrix, "my_fcn")]
... }
>>> mat = Matrix([[1, x]])
>>> octave_code(f(x) + g(x) + g(mat), user_functions=custom_functions)
existing_octave_fcn(x) + my_fcn(x) + my_mat_fcn([1 x])
Mathematica 代码打印机示例:
>>> x_ = Function('x')
>>> expr = x_(n*T) * sin((t - n*T) / T)
>>> expr = expr / ((-T*n + t) / T)
>>> expr⎛-T⋅n + t⎞
T⋅x(T⋅n)⋅sin⎜────────⎟⎝ T ⎠
──────────────────────-T⋅n + t>>> expr = summation(expr, (n, -1, 1))
>>> mathematica_code(expr)
T*(x[-T]*Sin[(T + t)/T]/(T + t) + x[T]*Sin[(-T + t)/T]/(-T + t) + x[0]*Sin[t/T]/t)
我们可以通过我们支持的不同语言中的常见表达式并看看它是如何工作的:
>>> k, g1, g2, r, I, S = symbols("k, gamma_1, gamma_2, r, I, S")
>>> expr = k * g1 * g2 / (r**3)
>>> expr = expr * 2 * I * S * (3 * (cos(beta))**2 - 1) / 2
>>> expr⎛ 2 ⎞
I⋅S⋅γ₁⋅γ₂⋅k⋅⎝3⋅cos (β) - 1⎠
───────────────────────────3r
>>> print(jscode(expr, assign_to="H_is"))
H_is = I*S*gamma_1*gamma_2*k*(3*Math.pow(Math.cos(beta), 2) - 1)/Math.pow(r, 3);
>>> print(ccode(expr, assign_to="H_is", standard='C89'))
H_is = I*S*gamma_1*gamma_2*k*(3*pow(cos(beta), 2) - 1)/pow(r, 3);
>>> print(fcode(expr, assign_to="H_is"))H_is = I*S*gamma_1*gamma_2*k*(3*cos(beta)**2 - 1)/r**3
>>> print(julia_code(expr, assign_to="H_is"))
H_is = I .* S .* gamma_1 .* gamma_2 .* k .* (3 * cos(beta) .^ 2 - 1) ./ r .^ 3
>>> print(octave_code(expr, assign_to="H_is"))
H_is = I.*S.*gamma_1.*gamma_2.*k.*(3*cos(beta).² - 1)./r.³;
>>> print(rust_code(expr, assign_to="H_is"))
H_is = I*S*gamma_1*gamma_2*k*(3*beta.cos().powi(2) - 1)/r.powi(3);
>>> print(mathematica_code(expr))
I*S*gamma_1*gamma_2*k*(3*Cos[beta]² - 1)/r³
Codegen(sympy.utilities.codegen)
此模块处理从 SymPy 表达式创建可编译代码。这比 autowrap 更低级,因为它实际上不尝试编译代码,但比打印机更高级,因为它生成可编译的文件(包括头文件),而不仅仅是代码片段。
在这里友好的函数是codegen
和make_routine
。codegen
接受(variable, expression)
对的列表和语言(支持 C、F95 和 Octave/Matlab)。它返回一个代码文件和一个头文件的字符串,对应于相关语言。这些变量被创建为返回表达式值的函数输出。
注意
可调用的codegen
不会自动存在于 sympy 命名空间中,要使用它,必须首先从sympy.utilities.codegen
导入codegen
。
例如:
>>> from sympy.utilities.codegen import codegen
>>> length, breadth, height = symbols('length, breadth, height')
>>> [(c_name, c_code), (h_name, c_header)] = \
... codegen(('volume', length*breadth*height), "C99", "test",
... header=False, empty=False)
>>> print(c_name)
test.c
>>> print(c_code)
#include "test.h"
#include <math.h>
double volume(double breadth, double height, double length) {double volume_result;volume_result = breadth*height*length;return volume_result;
}
>>> print(h_name)
test.h
>>> print(c_header)
#ifndef PROJECT__TEST__H
#define PROJECT__TEST__H
double volume(double breadth, double height, double length);
#endif
各种标志可以让您修改codegen
中的事物。使用project
可以变化项目名称以用于预处理指令。在global_vars
参数中列出的全局变量将不会显示为函数参数。
language
是一个不区分大小写的字符串,表示源代码语言。目前支持C
、F95
和Octave
。Octave
生成与 Octave 和 Matlab 兼容的代码。
当header
为 True 时,在每个源文件的顶部会写入一个头部。当empty
为 True 时,使用空行来结构化代码。使用argument_sequence
可以定义首选顺序的例程参数序列。
prefix
定义了包含源代码的文件名称的前缀。如果省略,则使用第一个name_expr
元组的名称。
当to_files
为 True 时,代码将被写入一个或多个带有给定前缀的文件。
这里是一个示例:
>>> [(f_name, f_code), header] = codegen(("volume", length*breadth*height),
... "F95", header=False, empty=False, argument_sequence=(breadth, length),
... global_vars=(height,))
>>> print(f_code)
REAL*8 function volume(breadth, length)
implicit none
REAL*8, intent(in) :: breadth
REAL*8, intent(in) :: length
volume = breadth*height*length
end function
方法make_routine
创建一个Routine
对象,表示一组表达式的评估例程。这仅适用于 CodeGen 对象内部使用,作为从 SymPy 表达式到生成代码的中间表示。不建议自己创建Routine
对象。您应该使用make_routine
方法。make_routine
反过来调用 CodeGen 对象的routine
方法,具体取决于所选语言。这样创建表示赋值等的内部对象,并使用它们创建Routine
类。
各种 codegen 对象,如Routine
和Variable
并非 SymPy 对象(它们不从 Basic 子类化)。
例如:
>>> from sympy.utilities.codegen import make_routine
>>> from sympy.physics.hydrogen import R_nl
>>> expr = R_nl(3, y, x, 6)
>>> routine = make_routine('my_routine', expr)
>>> [arg.result_var for arg in routine.results]
[result₅₁₄₂₃₄₁₆₈₁₃₉₇₇₁₉₄₂₈]
>>> [arg.expr for arg in routine.results]
⎡ __________ ⎤
⎢ y ╱ (2 - y)! -2⋅x ⎥
⎢4⋅√6⋅(4⋅x) ⋅ ╱ ──────── ⋅ℯ ⋅assoc_laguerre(2 - y, 2⋅y + 1, 4⋅x)⎥
⎢ ╲╱ (y + 3)! ⎥
⎢────────────────────────────────────────────────────────────────────⎥
⎣ 3 ⎦
>>> [arg.name for arg in routine.arguments]
[x, y]
另一个更复杂的示例,混合指定和自动分配的名称。还有矩阵输出:
>>> routine = make_routine('fcn', [x*y, Eq(a, 1), Eq(r, x + r), Matrix([[x, 2]])])
>>> [arg.result_var for arg in routine.results]
[result_5397460570204848505]
>>> [arg.expr for arg in routine.results]
[x⋅y]
>>> [arg.name for arg in routine.arguments]
[x, y, a, r, out_8598435338387848786]
我们可以更仔细地检查各种参数:
>>> from sympy.utilities.codegen import (InputArgument, OutputArgument,
... InOutArgument)
>>> [a.name for a in routine.arguments if isinstance(a, InputArgument)]
[x, y]>>> [a.name for a in routine.arguments if isinstance(a, OutputArgument)]
[a, out_8598435338387848786]
>>> [a.expr for a in routine.arguments if isinstance(a, OutputArgument)]
[1, [x 2]]>>> [a.name for a in routine.arguments if isinstance(a, InOutArgument)]
[r]
>>> [a.expr for a in routine.arguments if isinstance(a, InOutArgument)]
[r + x]
完整的 API 参考可以在这里查看。
Autowrap
Autowrap 自动生成代码,将其写入磁盘,编译它并导入到当前会话中。本模块的主要功能是autowrap
,binary_function
和ufuncify
。
它还会自动将包含Indexed
对象的表达式转换为求和。类 IndexedBase、Indexed 和 Idx 表示矩阵元素 M[i, j]。有关更多信息,请参见 Tensor。
autowrap
使用 f2py 或 Cython 创建包装器并创建数值函数。
注意
可调用的autowrap
不会自动放置在 sympy 命名空间中,要使用它,您必须先从sympy.utilities.autowrap
导入autowrap
。
autowrap
返回的可调用函数是一个二进制的 Python 函数,而不是 SymPy 对象。例如:
>>> from sympy.utilities.autowrap import autowrap
>>> expr = ((x - y + z)**(13)).expand()
>>> binary_func = autowrap(expr)
>>> binary_func(1, 4, 2)
-1.0
autowrap()提供的各种标志有助于修改方法提供的服务。参数tempdir
告诉 autowrap 在特定目录中编译代码,并在完成后保留文件。例如:
>>> from sympy.utilities.autowrap import autowrap
>>> from sympy.physics.qho_1d import psi_n
>>> x_ = IndexedBase('x')
>>> y_ = IndexedBase('y')
>>> m = symbols('m', integer=True)
>>> i = Idx('i', m)
>>> qho = autowrap(Eq(y_[i], psi_n(0, x_[i], m, omega)), tempdir='/tmp')
检查指定目录中的 Fortran 源代码揭示了这一点:
subroutine autofunc(m, omega, x, y)
implicit none
INTEGER*4, intent(in) :: m
REAL*8, intent(in) :: omega
REAL*8, intent(in), dimension(1:m) :: x
REAL*8, intent(out), dimension(1:m) :: y
INTEGER*4 :: iREAL*8, parameter :: hbar = 1.05457162d-34
REAL*8, parameter :: pi = 3.14159265358979d0
do i = 1, my(i) = (m*omega)**(1.0d0/4.0d0)*exp(-4.74126166983329d+33*m*omega*x(i &)**2)/(hbar**(1.0d0/4.0d0)*pi**(1.0d0/4.0d0))
end doend subroutine
使用参数args
与其一起改变参数序列:
>>> eq = Eq(y_[i], psi_n(0, x_[i], m, omega))
>>> qho = autowrap(eq, tempdir='/tmp', args=[y, x, m, omega])
产生:
subroutine autofunc(y, x, m, omega)
implicit none
INTEGER*4, intent(in) :: m
REAL*8, intent(in) :: omega
REAL*8, intent(out), dimension(1:m) :: y
REAL*8, intent(in), dimension(1:m) :: x
INTEGER*4 :: iREAL*8, parameter :: hbar = 1.05457162d-34
REAL*8, parameter :: pi = 3.14159265358979d0
do i = 1, my(i) = (m*omega)**(1.0d0/4.0d0)*exp(-4.74126166983329d+33*m*omega*x(i &)**2)/(hbar**(1.0d0/4.0d0)*pi**(1.0d0/4.0d0))
end doend subroutine
参数verbose
是布尔值,可选,如果为 True,则 autowrap 不会使命令行后端静音。这对调试非常有帮助。
参数language
和backend
用于将默认值从Fortran
和f2py
更改为C
和Cython
。参数 helpers 用于定义主表达式所需的辅助表达式。如果主表达式需要调用特殊函数,则应将其放入 helpers 可迭代对象中。Autowrap 将确保编译的主表达式可以链接到帮助程序例程。项目应该是带有(<function_name>,<sympy_expression>,
在 autowrap
层面可用的另一种方法是 binary_function
。它返回一个 sympy 函数。其优势在于,与 SymPy 速度相比,我们可以得到非常快速的函数。这是因为我们将使用带有 SymPy 属性和方法的编译函数。一个例子:
>>> from sympy.utilities.autowrap import binary_function
>>> from sympy.physics.hydrogen import R_nl
>>> psi_nl = R_nl(1, 0, a, r)
>>> f = binary_function('f', psi_nl)
>>> f(a, r).evalf(3, subs={a: 1, r: 2})
0.766
虽然 NumPy 操作对于矢量化数据非常高效,但在连锁操作时有时会产生不必要的成本。考虑以下操作
>>> x = get_numpy_array(...)
>>> y = sin(x) / x
sin
和 /
运算符调用执行紧密循环的 C
例程。得到的计算看起来像这样
for(int i = 0; i < n; i++)
{temp[i] = sin(x[i]);
}
for(int i = i; i < n; i++)
{y[i] = temp[i] / x[i];
}
这略微不够优化,因为
-
我们分配了额外的
temp
数组 -
当一次就足够时,我们对
x
内存进行了两次遍历
更好的解决方案将两个逐元素操作融合为单个循环
for(int i = i; i < n; i++)
{y[i] = sin(x[i]) / x[i];
}
像 NumPy 这样的静态编译项目无法利用这些优化。幸运的是,SymPy 能够生成高效的低级别 C 或 Fortran 代码。然后,它可以依赖于像 Cython
或 f2py
这样的项目来编译并重新连接该代码回到 Python。幸运的是,这个过程是自动化的,希望利用这些代码生成的 SymPy 用户应该调用 ufuncify
函数。
ufuncify
是 Aurowrap 模块的第三种可用方法。它基本上暗示了‘通用函数’,并遵循了 NumPy 设定的理念。与 autowrap 相比,ufuncify 的主要优点是允许数组作为参数,并且可以逐元素地进行操作。按照 NumPy 的数组广播规则逐元素进行的核心操作。查看 此 了解更多信息。
>>> from sympy import *
>>> from sympy.abc import x
>>> expr = sin(x)/x
>>> from sympy.utilities.autowrap import ufuncify
>>> f = ufuncify([x], expr)
此函数 f
消耗并返回一个 NumPy 数组。通常情况下,ufuncify
的性能至少与 lambdify
相当。如果表达式复杂,则 ufuncify
通常明显优于 NumPy 支持的解决方案。Jensen 在这个主题上有一篇很好的 博文。
让我们看一个进行一些数量分析的示例:
>>> from sympy.physics.hydrogen import R_nl
>>> expr = R_nl(3, 1, x, 6)
>>> expr-2⋅x
8⋅x⋅(4 - 4⋅x)⋅ℯ
───────────────────3
lambdify
函数将 SymPy 表达式转换为 Python 函数,利用各种数值库。默认情况下,lambdify
依赖于 math
标准库中的实现。自然地,原始的 Python 比 SymPy 更快。但它也支持 mpmath
和最显著的是 numpy
。使用 NumPy 库使生成的函数能够访问由编译的 C 代码支持的强大的矢量化 ufuncs。
让我们比较速度:
>>> from sympy.utilities.autowrap import ufuncify
>>> from sympy.utilities.lambdify import lambdify
>>> fn_numpy = lambdify(x, expr, 'numpy')
>>> fn_fortran = ufuncify([x], expr, backend='f2py')
>>> from numpy import linspace
>>> xx = linspace(0, 1, 5)
>>> fn_numpy(xx)
[ 0\. 1.21306132 0.98101184 0.44626032 0\. ]
>>> fn_fortran(xx)
[ 0\. 1.21306132 0.98101184 0.44626032 0\. ]
>>> import timeit
>>> timeit.timeit('fn_numpy(xx)', 'from __main__ import fn_numpy, xx', number=10000)
0.18891601900395472
>>> timeit.timeit('fn_fortran(xx)', 'from __main__ import fn_fortran, xx', number=10000)
0.004707066000264604
与 autowrap
可用的选项基本相同。
SymPy 还有其他有效的数值计算工具可用。参见 此 页面进行比较。
重写表达式的类和函数(sympy.codegen.rewriting)
有助于重写表达式以进行优化代码生成的类和函数。某些语言(或其标准),如 C99,提供了专门的数学函数以获得更好的性能和/或精度。
使用此模块中的optimize
函数以及一系列规则(表示为Optimization
实例),可以为此目的重写表达式:
>>> from sympy import Symbol, exp, log
>>> from sympy.codegen.rewriting import optimize, optims_c99
>>> x = Symbol('x')
>>> optimize(3*exp(2*x) - 3, optims_c99)
3*expm1(2*x)
>>> optimize(exp(2*x) - 1 - exp(-33), optims_c99)
expm1(2*x) - exp(-33)
>>> optimize(log(3*x + 3), optims_c99)
log1p(x) + log(3)
>>> optimize(log(2*x + 3), optims_c99)
log(2*x + 3)
上面导入的optims_c99
是包含以下实例的元组(可以从sympy.codegen.rewriting
导入):
-
expm1_opt
-
log1p_opt
-
exp2_opt
-
log2_opt
-
log2const_opt
class sympy.codegen.rewriting.FuncMinusOneOptim(func, func_m_1, opportunistic=True)
专门用于计算“f(x) - 1”的 ReplaceOptim 特化函数。
参数:
函数:
减一的函数。
func_m_1:
专门用于评估
func(x) - 1
的函数。
机会主义:布尔值
当为
True
时,只要剩余数字项的数量减少,则应用转换。当为False
时,仅当完全消除数字项时才应用转换。
解释
当 x 趋近于零时,以向一处收敛的数值函数通常最好通过专门的函数实现,以避免灾难性的取消。一个典型的例子是 C 标准库中的expm1(x)
,它计算的是exp(x) - 1
。这种函数在其参数远小于一时能保留更多有效位数,相比之下,后续减一则无法保留这些位数。
示例
>>> from sympy import symbols, exp
>>> from sympy.codegen.rewriting import FuncMinusOneOptim
>>> from sympy.codegen.cfunctions import expm1
>>> x, y = symbols('x y')
>>> expm1_opt = FuncMinusOneOptim(exp, expm1)
>>> expm1_opt(exp(x) + 2*exp(5*y) - 3)
expm1(x) + 2*expm1(5*y)
replace_in_Add(e)
作为第二个参数传递给 Basic.replace(…)
class sympy.codegen.rewriting.Optimization(cost_function=None, priority=1)
重写优化的抽象基类。
子类应实现__call__
,接受一个表达式作为参数。
参数:
成本函数:返回数字的可调用对象
优先级:数字
class sympy.codegen.rewriting.ReplaceOptim(query, value, **kwargs)
在表达式上调用 replace 进行重写优化。
参数:
查询:
替换时传递的第一个参数。
值:
替换的第二个传递参数。
解释
可以将该实例用作函数,适用于表达式,它将应用于replace
方法(参见sympy.core.basic.Basic.replace()
)。
示例
>>> from sympy import Symbol
>>> from sympy.codegen.rewriting import ReplaceOptim
>>> from sympy.codegen.cfunctions import exp2
>>> x = Symbol('x')
>>> exp2_opt = ReplaceOptim(lambda p: p.is_Pow and p.base == 2,
... lambda p: exp2(p.exp))
>>> exp2_opt(2**x)
exp2(x)
sympy.codegen.rewriting.create_expand_pow_optimization(limit, *, base_req=<function <lambda>>)
为Pow
展开创建一个ReplaceOptim
实例。
参数:
限制:整数
展开为乘法的最高幂次。
base_req:返回布尔值的函数
展开发生的基本要求,默认返回基数的
is_symbol
属性。
解释
展开的要求是基数必须是一个符号,并且指数必须是一个整数(且小于或等于limit
)。
示例
>>> from sympy import Symbol, sin
>>> from sympy.codegen.rewriting import create_expand_pow_optimization
>>> x = Symbol('x')
>>> expand_opt = create_expand_pow_optimization(3)
>>> expand_opt(x**5 + x**3)
x**5 + x*x*x
>>> expand_opt(x**5 + x**3 + sin(x)**3)
x**5 + sin(x)**3 + x*x*x
>>> opt2 = create_expand_pow_optimization(3, base_req=lambda b: not b.is_Function)
>>> opt2((x+1)**2 + sin(x)**2)
sin(x)**2 + (x + 1)*(x + 1)
sympy.codegen.rewriting.optimize(expr, optimizations)
对表达式应用优化。
参数:
表达式:表达式
优化:Optimization
实例的可迭代对象
优化将根据
priority
(最高优先级在前)排序。
示例
>>> from sympy import log, Symbol
>>> from sympy.codegen.rewriting import optims_c99, optimize
>>> x = Symbol('x')
>>> optimize(log(x+3)/log(2) + log(x**2 + 1), optims_c99)
log1p(x**2) + log2(x + 3)
用于矩阵操作的额外 AST 节点。此模块中的节点旨在表示无法通过 SymPy 表达式表示的代码生成目标语言中的矩阵表达式的优化。
例如,我们可以使用sympy.codegen.rewriting.optimize()
和sympy.codegen.rewriting
提供的matin_opt
优化来在特定假设下转换矩阵乘法:
>>> from sympy import symbols, MatrixSymbol
>>> n = symbols('n', integer=True)
>>> A = MatrixSymbol('A', n, n)
>>> x = MatrixSymbol('x', n, 1)
>>> expr = A**(-1) * x
>>> from sympy import assuming, Q
>>> from sympy.codegen.rewriting import matinv_opt, optimize
>>> with assuming(Q.fullrank(A)):
... optimize(expr, [matinv_opt])
MatrixSolve(A, vector=x)
class sympy.codegen.matrix_nodes.MatrixSolve(*args, **kwargs)
代表一个解线性矩阵方程的操作。
参数:
matrix : MatrixSymbol
表示线性方程中变量系数的矩阵。该矩阵必须是方阵且满秩(即所有列必须线性独立),才能进行有效的求解操作。
vector : MatrixSymbol
表示在
matrix
中表示的方程的解的单列矩阵。
示例
>>> from sympy import symbols, MatrixSymbol
>>> from sympy.codegen.matrix_nodes import MatrixSolve
>>> n = symbols('n', integer=True)
>>> A = MatrixSymbol('A', n, n)
>>> x = MatrixSymbol('x', n, 1)
>>> from sympy.printing.numpy import NumPyPrinter
>>> NumPyPrinter().doprint(MatrixSolve(A, x))
'numpy.linalg.solve(A, x)'
>>> from sympy import octave_code
>>> octave_code(MatrixSolve(A, x))
'A \\ x'
``` ## 使用近似简化表达式的工具(sympy.codegen.approximations)```py
class sympy.codegen.approximations.SeriesApprox(bounds, reltol, max_order=4, n_point_checks=4, **kwargs)
通过将它们展开为级数来近似函数。
参数:
bounds : dict
将表达式映射到长度为 2 的边界元组(低,高)。
reltol : number
相对于所有边界中的最大下界时要忽略术语。
max_order : int
包括在级数展开中的最大阶数
n_point_checks : int(偶数)
在离散点(线性间隔在变量的边界上)上检查展开的有效性(相对于 reltol)。在此数值检查中使用的点数由此数字给出。
示例
>>> from sympy import sin, pi
>>> from sympy.abc import x, y
>>> from sympy.codegen.rewriting import optimize
>>> from sympy.codegen.approximations import SeriesApprox
>>> bounds = {x: (-.1, .1), y: (pi-1, pi+1)}
>>> series_approx2 = SeriesApprox(bounds, reltol=1e-2)
>>> series_approx3 = SeriesApprox(bounds, reltol=1e-3)
>>> series_approx8 = SeriesApprox(bounds, reltol=1e-8)
>>> expr = sin(x)*sin(y)
>>> optimize(expr, [series_approx2])
x*(-y + (y - pi)**3/6 + pi)
>>> optimize(expr, [series_approx3])
(-x**3/6 + x)*sin(y)
>>> optimize(expr, [series_approx8])
sin(x)*sin(y)
class sympy.codegen.approximations.SumApprox(bounds, reltol, **kwargs)
通过忽略小术语来近似和。
参数:
bounds : dict
将表达式映射到长度为 2 的边界元组(低,高)。
reltol : number
相对于所有边界中的最大下界时要忽略术语。
解释
如果术语是可以确定为单调的表达式,则将这些表达式的边界添加进去。
示例
>>> from sympy import exp
>>> from sympy.abc import x, y, z
>>> from sympy.codegen.rewriting import optimize
>>> from sympy.codegen.approximations import SumApprox
>>> bounds = {x: (-1, 1), y: (1000, 2000), z: (-10, 3)}
>>> sum_approx3 = SumApprox(bounds, reltol=1e-3)
>>> sum_approx2 = SumApprox(bounds, reltol=1e-2)
>>> sum_approx1 = SumApprox(bounds, reltol=1e-1)
>>> expr = 3*(x + y + exp(z))
>>> optimize(expr, [sum_approx3])
3*(x + y + exp(z))
>>> optimize(expr, [sum_approx2])
3*y + 3*exp(z)
>>> optimize(expr, [sum_approx1])
3*y
``` ## 抽象语法树类(sympy.codegen.ast)用于表示完整函数/模块的 AST 类型。大多数类型都很小,仅用作 AST 中的标记。下面的树形图说明了 AST 类型之间的关系。### AST 类型树```py
*Basic*||CodegenAST||--->AssignmentBase| |--->Assignment| |--->AugmentedAssignment| |--->AddAugmentedAssignment| |--->SubAugmentedAssignment| |--->MulAugmentedAssignment| |--->DivAugmentedAssignment| |--->ModAugmentedAssignment||--->CodeBlock|||--->Token|--->Attribute|--->For|--->String| |--->QuotedString| |--->Comment|--->Type| |--->IntBaseType| | |--->_SizedIntType| | |--->SignedIntType| | |--->UnsignedIntType| |--->FloatBaseType| |--->FloatType| |--->ComplexBaseType| |--->ComplexType|--->Node| |--->Variable| | |---> Pointer| |--->FunctionPrototype| |--->FunctionDefinition|--->Element|--->Declaration|--->While|--->Scope|--->Stream|--->Print|--->FunctionCall|--->BreakToken|--->ContinueToken|--->NoneToken|--->Return
预定义类型
sympy.codegen.ast
模块提供了多种Type
实例供方便使用。也许最常见的两种用于代码生成(数值代码)的是float32
和float64
(分别称为单精度和双精度)。此外,还有精确的通用类型版本(在打印时选择底层数据类型):real
、integer
、complex_
、bool_
。
其他定义的Type
实例为:
-
intc
:C 中使用的整数类型。 -
intp
:C 中使用的无符号整数类型。 -
int8
、int16
、int32
、int64
:n 位整数。 -
uint8
、uint16
、uint32
、uint64
:n 位无符号整数。 -
float80
:在现代 x86/amd64 硬件上称为“扩展精度”。 -
complex64
:由两个float32
数字表示的复数。 -
complex128
:由两个float64
数字表示的复数
使用节点
可以使用 AST 节点构造简单的算法。让我们构造一个应用牛顿法的循环:
>>> from sympy import symbols, cos
>>> from sympy.codegen.ast import While, Assignment, aug_assign, Print, QuotedString
>>> t, dx, x = symbols('tol delta val')
>>> expr = cos(x) - x**3
>>> whl = While(abs(dx) > t, [
... Assignment(dx, -expr/expr.diff(x)),
... aug_assign(x, '+', dx),
... Print([x])
... ])
>>> from sympy import pycode
>>> py_str = pycode(whl)
>>> print(py_str)
while (abs(delta) > tol):delta = (val**3 - math.cos(val))/(-3*val**2 - math.sin(val))val += deltaprint(val)
>>> import math
>>> tol, val, delta = 1e-5, 0.5, float('inf')
>>> exec(py_str)
1.1121416371
0.909672693737
0.867263818209
0.865477135298
0.865474033111
>>> print('%3.1g' % (math.cos(val) - val**3))
-3e-11
如果我们想要为相同的 while 循环生成 Fortran 代码,我们只需调用 fcode
:
>>> from sympy import fcode
>>> print(fcode(whl, standard=2003, source_format='free'))
do while (abs(delta) > tol)delta = (val**3 - cos(val))/(-3*val**2 - sin(val))val = val + deltaprint *, val
end do
有一个函数在 sympy.codegen.algorithms
中构造循环(或完整函数)。
class sympy.codegen.ast.Assignment(lhs, rhs)
代表用于代码生成的变量赋值。
参数:
lhs:Expr
SymPy 对象,表示表达式的左手边。这些应该是单一的对象,例如在编写代码时使用的对象。显著的类型包括 Symbol、MatrixSymbol、MatrixElement 和 Indexed。支持这些类型的子类也是支持的。
rhs:Expr
SymPy 对象,表示表达式的右手边。可以是任何类型,只要其形状与左手边相对应。例如,Matrix 类型可以分配给 MatrixSymbol,但不能分配给 Symbol,因为维度不会对齐。
示例
>>> from sympy import symbols, MatrixSymbol, Matrix
>>> from sympy.codegen.ast import Assignment
>>> x, y, z = symbols('x, y, z')
>>> Assignment(x, y)
Assignment(x, y)
>>> Assignment(x, 0)
Assignment(x, 0)
>>> A = MatrixSymbol('A', 1, 3)
>>> mat = Matrix([x, y, z]).T
>>> Assignment(A, mat)
Assignment(A, Matrix([[x, y, z]]))
>>> Assignment(A[0, 1], x)
Assignment(A[0, 1], x)
class sympy.codegen.ast.AssignmentBase(lhs, rhs)
赋值和增强赋值的抽象基类。
属性:
opstr
用于赋值操作符的符号,例如“=”,“+=”等。
class sympy.codegen.ast.Attribute(possibly parametrized)
用于 sympy.codegen.ast.Node
的实例(它将 attrs
作为 Attribute
的实例)。
参数:
name:str
parameters:Tuple
示例
>>> from sympy.codegen.ast import Attribute
>>> volatile = Attribute('volatile')
>>> volatile
volatile
>>> print(repr(volatile))
Attribute(String('volatile'))
>>> a = Attribute('foo', [1, 2, 3])
>>> a
foo(1, 2, 3)
>>> a.parameters == (1, 2, 3)
True
class sympy.codegen.ast.AugmentedAssignment(lhs, rhs)
增强赋值的基类。
属性:
binopstr
用于赋值操作中应用的二元操作符的符号,例如“+”,“*”等。
class sympy.codegen.ast.BreakToken(*args, **kwargs)
表示 C/Python 中的 ‘break’(Fortran 中的 ‘exit’)。
使用预定义的实例 break_
或手动实例化。
示例
>>> from sympy import ccode, fcode
>>> from sympy.codegen.ast import break_
>>> ccode(break_)
'break'
>>> fcode(break_, source_format='free')
'exit'
class sympy.codegen.ast.CodeBlock(*args)
代表代码块。
解释
目前仅支持赋值。这个限制将来会解除。
此对象的有用属性包括:
left_hand_sides
:
按顺序的赋值的左手边的元组。
left_hand_sides
:
按顺序的右手边的赋值的元组。
free_symbols
:右侧表达式的自由符号
在赋值的左手边未出现的符号。
此对象的有用方法包括:
topological_sort
:
类方法。返回一个按照变量被赋值前使用的顺序排序的代码块。
cse
:
返回一个新的代码块,消除常见子表达式并将其作为赋值语句提取出来。
示例
>>> from sympy import symbols, ccode
>>> from sympy.codegen.ast import CodeBlock, Assignment
>>> x, y = symbols('x y')
>>> c = CodeBlock(Assignment(x, 1), Assignment(y, x + 1))
>>> print(ccode(c))
x = 1;
y = x + 1;
cse(symbols=None, optimizations=None, postprocess=None, order='canonical')
返回一个新的代码块,消除常见的子表达式。
解释
查看 sympy.simplify.cse_main.cse()
的文档字符串获取更多信息。
示例
>>> from sympy import symbols, sin
>>> from sympy.codegen.ast import CodeBlock, Assignment
>>> x, y, z = symbols('x y z')
>>> c = CodeBlock(
... Assignment(x, 1),
... Assignment(y, sin(x) + 1),
... Assignment(z, sin(x) - 1),
... )
...
>>> c.cse()
CodeBlock(Assignment(x, 1),Assignment(x0, sin(x)),Assignment(y, x0 + 1),Assignment(z, x0 - 1)
)
classmethod topological_sort(assignments)
返回一个按拓扑顺序排序的代码块,以便变量在使用之前被赋值。
示例
尽可能保持现有的赋值顺序。
此函数假设变量只被分配一次。
这是一个类构造函数,因此当变量在分配之前被使用时,默认构造函数可以报错。
>>> from sympy import symbols
>>> from sympy.codegen.ast import CodeBlock, Assignment
>>> x, y, z = symbols('x y z')
>>> assignments = [
... Assignment(x, y + z),
... Assignment(y, z + 1),
... Assignment(z, 2),
... ]
>>> CodeBlock.topological_sort(assignments)
CodeBlock(Assignment(z, 2),Assignment(y, z + 1),Assignment(x, y + z)
)
class sympy.codegen.ast.Comment(*args, **kwargs)
表示一个注释。
class sympy.codegen.ast.ComplexType(*args, **kwargs)
表示复杂的浮点数。
class sympy.codegen.ast.ContinueToken(*args, **kwargs)
在 C/Python 中表示‘continue’(在 Fortran 中表示‘cycle’)
使用预制实例continue_
或手动实例化。
示例
>>> from sympy import ccode, fcode
>>> from sympy.codegen.ast import continue_
>>> ccode(continue_)
'continue'
>>> fcode(continue_, source_format='free')
'cycle'
class sympy.codegen.ast.Declaration(*args, **kwargs)
表示变量声明
参数:
variable : 变量
示例
>>> from sympy.codegen.ast import Declaration, NoneToken, untyped
>>> z = Declaration('z')
>>> z.variable.type == untyped
True
>>> # value is special NoneToken() which must be tested with == operator
>>> z.variable.value is None # won't work
False
>>> z.variable.value == None # not PEP-8 compliant
True
>>> z.variable.value == NoneToken() # OK
True
class sympy.codegen.ast.Element(*args, **kwargs)
数组中的元素(可能是 N 维)。
示例
>>> from sympy.codegen.ast import Element
>>> elem = Element('x', 'ijk')
>>> elem.symbol.name == 'x'
True
>>> elem.indices
(i, j, k)
>>> from sympy import ccode
>>> ccode(elem)
'x[i][j][k]'
>>> ccode(Element('x', 'ijk', strides='lmn', offset='o'))
'x[i*l + j*m + k*n + o]'
class sympy.codegen.ast.FloatBaseType(*args, **kwargs)
表示浮点数类型。
cast_nocheck
别名为Float
的浮点类型
class sympy.codegen.ast.FloatType(*args, **kwargs)
表示具有固定位宽的浮点类型。
基数 2 和一个符号位是默认的。
参数:
name : 字符串
类型的名称。
nbits : 整数
用于表示的位数(存储)。
nmant : 整数
用于表示尾数的位数。
nexp : 整数
用于表示尾数的位数。
示例
>>> from sympy import S
>>> from sympy.codegen.ast import FloatType
>>> half_precision = FloatType('f16', nbits=16, nmant=10, nexp=5)
>>> half_precision.max
65504
>>> half_precision.tiny == S(2)**-14
True
>>> half_precision.eps == S(2)**-10
True
>>> half_precision.dig == 3
True
>>> half_precision.decimal_dig == 5
True
>>> half_precision.cast_check(1.0)
1.0
>>> half_precision.cast_check(1e5)
Traceback (most recent call last):...
ValueError: Maximum value for data type smaller than new value.
cast_nocheck(value)
强制转换,而不检查是否超出范围或子正常。
property decimal_dig
需要存储和加载而不会丢失的位数。
解释
需要保证两个连续的转换(浮点数 -> 文本 -> 浮点数)是幂等的十进制数字的数量。这在存储浮点值作为文本时,由于四舍五入误差而不想丢失精度时非常有用。
property dig
保证在文本中保留的十进制数字的数量。
当转换文本 -> 浮点数 -> 文本时,可以保证至少保留dig
位数,以便于舍入或溢出。
property eps
1.0 与下一个可表示值之间的差异。
property max
可表示的最大值。
property max_exponent
最大的正数 n,使得 2 ** (n - 1)是可表示的有限值。
property min_exponent
最小的负数 n,使得 2 ** (n - 1)是有效的正规化数。
property tiny
最小的正规化值。
class sympy.codegen.ast.For(*args, **kwargs)
在代码中表示‘for-loop’。
表达式的形式:
“for target in iter:
body…”
参数:
target : 符号
iter : 可迭代体 body : CodeBlock 或可迭代
!当传入一个可迭代对象时,它用于实例化 CodeBlock。
示例
>>> from sympy import symbols, Range
>>> from sympy.codegen.ast import aug_assign, For
>>> x, i, j, k = symbols('x i j k')
>>> for_i = For(i, Range(10), [aug_assign(x, '+', i*j*k)])
>>> for_i
For(i, iterable=Range(0, 10, 1), body=CodeBlock(AddAugmentedAssignment(x, i*j*k)
))
>>> for_ji = For(j, Range(7), [for_i])
>>> for_ji
For(j, iterable=Range(0, 7, 1), body=CodeBlock(For(i, iterable=Range(0, 10, 1), body=CodeBlock(AddAugmentedAssignment(x, i*j*k)))
))
>>> for_kji =For(k, Range(5), [for_ji])
>>> for_kji
For(k, iterable=Range(0, 5, 1), body=CodeBlock(For(j, iterable=Range(0, 7, 1), body=CodeBlock(For(i, iterable=Range(0, 10, 1), body=CodeBlock(AddAugmentedAssignment(x, i*j*k)))))
))
class sympy.codegen.ast.FunctionCall(*args, **kwargs)
表示调用代码中的函数。
参数:
name : 字符串
function_args : 元组
示例
>>> from sympy.codegen.ast import FunctionCall
>>> from sympy import pycode
>>> fcall = FunctionCall('foo', 'bar baz'.split())
>>> print(pycode(fcall))
foo(bar, baz)
class sympy.codegen.ast.FunctionDefinition(*args, **kwargs)
表示代码中的函数定义。
参数:
return_type : 类型
name : 字符串
parameters: 变量实例的可迭代对象
body : CodeBlock 或可迭代
attrs : 属性实例的可迭代对象
示例
>>> from sympy import ccode, symbols
>>> from sympy.codegen.ast import real, FunctionPrototype
>>> x, y = symbols('x y', real=True)
>>> fp = FunctionPrototype(real, 'foo', [x, y])
>>> ccode(fp)
'double foo(double x, double y)'
>>> from sympy.codegen.ast import FunctionDefinition, Return
>>> body = [Return(x*y)]
>>> fd = FunctionDefinition.from_FunctionPrototype(fp, body)
>>> print(ccode(fd))
double foo(double x, double y){return x*y;
}
class sympy.codegen.ast.FunctionPrototype(*args, **kwargs)
表示函数原型
允许用户生成例如 C/C++中的前向声明。
参数:
return_type : 类型
name : 字符串
parameters: 变量实例的可迭代对象
attrs : 属性实例的可迭代对象
示例
>>> from sympy import ccode, symbols
>>> from sympy.codegen.ast import real, FunctionPrototype
>>> x, y = symbols('x y', real=True)
>>> fp = FunctionPrototype(real, 'foo', [x, y])
>>> ccode(fp)
'double foo(double x, double y)'
class sympy.codegen.ast.IntBaseType(*args, **kwargs)
整数基本类型,不包含大小信息。
class sympy.codegen.ast.Node(*args, **kwargs)
Token 的子类,携带属性‘attrs’(元组)
示例
>>> from sympy.codegen.ast import Node, value_const, pointer_const
>>> n1 = Node([value_const])
>>> n1.attr_params('value_const') # get the parameters of attribute (by name)
()
>>> from sympy.codegen.fnodes import dimension
>>> n2 = Node([value_const, dimension(5, 3)])
>>> n2.attr_params(value_const) # get the parameters of attribute (by Attribute instance)
()
>>> n2.attr_params('dimension') # get the parameters of attribute (by name)
(5, 3)
>>> n2.attr_params(pointer_const) is None
True
attr_params(looking_for)
返回 self.attrs 中名为looking_for
的属性的参数。
class sympy.codegen.ast.NoneToken(*args, **kwargs)
Python 的 NoneType 的 AST 等价物
Python 中对应的None
的实例是none
。
示例
>>> from sympy.codegen.ast import none, Variable
>>> from sympy import pycode
>>> print(pycode(Variable('x').as_Declaration(value=none)))
x = None
class sympy.codegen.ast.Pointer(*args, **kwargs)
表示指针。参见 Variable
。
示例
可以创建 Element
的实例:
>>> from sympy import Symbol
>>> from sympy.codegen.ast import Pointer
>>> i = Symbol('i', integer=True)
>>> p = Pointer('x')
>>> p[i+1]
Element(x, indices=(i + 1,))
class sympy.codegen.ast.Print(*args, **kwargs)
表示代码中的打印命令。
参数:
formatstring : 字符串
*args : Basic 实例(或通过 sympify 转换为这样的实例)
示例
>>> from sympy.codegen.ast import Print
>>> from sympy import pycode
>>> print(pycode(Print('x y'.split(), "coordinate: %12.5g %12.5g\\n")))
print("coordinate: %12.5g %12.5g\n" % (x, y), end="")
class sympy.codegen.ast.QuotedString(*args, **kwargs)
表示应使用引号打印的字符串。
class sympy.codegen.ast.Raise(*args, **kwargs)
在 Python 中打印为 ‘raise …’,在 C++ 中为 ‘throw …’。
class sympy.codegen.ast.Return(*args, **kwargs)
表示代码中的返回命令。
参数:
return : Basic
示例
>>> from sympy.codegen.ast import Return
>>> from sympy.printing.pycode import pycode
>>> from sympy import Symbol
>>> x = Symbol('x')
>>> print(pycode(Return(x)))
return x
class sympy.codegen.ast.RuntimeError_(*args, **kwargs)
在 C++ 中表示 ‘std::runtime_error’,在 Python 中表示 ‘RuntimeError’。
注意后者不常见,你可能想使用例如 ValueError。
class sympy.codegen.ast.Scope(*args, **kwargs)
表示代码中的作用域。
参数:
body : CodeBlock 或可迭代对象
当传递一个可迭代对象时,它用于实例化一个 CodeBlock。
class sympy.codegen.ast.SignedIntType(*args, **kwargs)
表示有符号整数类型。
class sympy.codegen.ast.Stream(*args, **kwargs)
表示流。
有两个预定义的 Stream 实例 stdout
和 stderr
。
参数:
name : str
示例
>>> from sympy import pycode, Symbol
>>> from sympy.codegen.ast import Print, stderr, QuotedString
>>> print(pycode(Print(['x'], file=stderr)))
print(x, file=sys.stderr)
>>> x = Symbol('x')
>>> print(pycode(Print([QuotedString('x')], file=stderr))) # print literally "x"
print("x", file=sys.stderr)
class sympy.codegen.ast.String(*args, **kwargs)
表示字符串的 SymPy 对象。
不是表达式的原子对象(与 Symbol 相反)。
参数:
text : 字符串
示例
>>> from sympy.codegen.ast import String
>>> f = String('foo')
>>> f
foo
>>> str(f)
'foo'
>>> f.text
'foo'
>>> print(repr(f))
String('foo')
class sympy.codegen.ast.Token(*args, **kwargs)
AST 类型的基类。
解释
在 _fields
中设置定义字段。属性(在 _fields 中定义)只允许包含 Basic 的实例(除非是原子的,见 String
)。__new__()
的参数与按 py_fields`. The ``defaults
类属性中定义顺序的属性相对应的顺序一致,是一个将属性名称映射到其默认值的字典的类属性。
子类不应需要覆盖 __new__()
方法。它们可以为传递给 __new__()
的每个属性定义名为 _construct_<attr>
的类或静态方法,以处理传递给 Basic
的值。列在类属性 not_in_args
中的属性不会传递给。
kwargs(exclude=(), apply=None)
将实例的属性作为关键字参数的字典获取。
参数:
exclude : 字符串集合
要排除的关键字集合。
apply : 可调用对象,可选
应用于所有值的函数。
class sympy.codegen.ast.Type(*args, **kwargs)
表示类型。
参数:
name : 字符串
类型的名称,例如
object
,int16
,float16
(后两者将分别使用Type
子类IntType
和FloatType
)。如果给定Type
实例,则返回该实例。
解释
命名是 NumPy 命名的超集。Type 类具有类方法 from_expr
,用于提供类型推导。它还具有 cast_check
方法,用于将参数强制转换为其类型,如果舍入误差不在容差范围内或者值无法由底层数据类型表示(例如无符号整数),可能会引发异常。
示例
>>> from sympy.codegen.ast import Type
>>> t = Type.from_expr(42)
>>> t
integer
>>> print(repr(t))
IntBaseType(String('integer'))
>>> from sympy.codegen.ast import uint8
>>> uint8.cast_check(-1)
Traceback (most recent call last):...
ValueError: Minimum value for data type bigger than new value.
>>> from sympy.codegen.ast import float32
>>> v6 = 0.123456
>>> float32.cast_check(v6)
0.123456
>>> v10 = 12345.67894
>>> float32.cast_check(v10)
Traceback (most recent call last):...
ValueError: Casting gives a significantly different value.
>>> boost_mp50 = Type('boost::multiprecision::cpp_dec_float_50')
>>> from sympy import cxxcode
>>> from sympy.codegen.ast import Declaration, Variable
>>> cxxcode(Declaration(Variable('x', type=boost_mp50)))
'boost::multiprecision::cpp_dec_float_50 x'
引用
[R39]
numpy.org/doc/stable/user/basics.types.html
cast_check(value, rtol=None, atol=0, precision_targets=None)
将值转换为实例的数据类型。
参数:
value : 数字
rtol : 浮点数
相对容差。(如果未给出则将推导)
atol : 浮点数
绝对容差(除了
rtol
)。
type_aliases : dict
用于类型映射的替换,例如
示例
>>> from sympy.codegen.ast import integer, float32, int8
>>> integer.cast_check(3.0) == 3
True
>>> float32.cast_check(1e-40)
Traceback (most recent call last):...
ValueError: Minimum value for data type bigger than new value.
>>> int8.cast_check(256)
Traceback (most recent call last):...
ValueError: Maximum value for data type smaller than new value.
>>> v10 = 12345.67894
>>> float32.cast_check(v10)
Traceback (most recent call last):...
ValueError: Casting gives a significantly different value.
>>> from sympy.codegen.ast import float64
>>> float64.cast_check(v10)
12345.67894
>>> from sympy import Float
>>> v18 = Float('0.123456789012345646')
>>> float64.cast_check(v18)
Traceback (most recent call last):...
ValueError: Casting gives a significantly different value.
>>> from sympy.codegen.ast import float80
>>> float80.cast_check(v18)
0.123456789012345649
classmethod from_expr(expr)
从表达式或Symbol
推断类型。
参数:
expr : 数字或 SymPy 对象
类型将从类型或属性推断。
引发:
类型推断失败时引发 ValueError。
示例
>>> from sympy.codegen.ast import Type, integer, complex_
>>> Type.from_expr(2) == integer
True
>>> from sympy import Symbol
>>> Type.from_expr(Symbol('z', complex=True)) == complex_
True
>>> Type.from_expr(sum)
Traceback (most recent call last):...
ValueError: Could not deduce type from expr.
class sympy.codegen.ast.UnsignedIntType(*args, **kwargs)
表示一个无符号整数类型。
class sympy.codegen.ast.Variable(*args, **kwargs)
表示一个变量。
参数:
symbol : Symbol
type : Type(可选)
变量的类型。
attrs : 可迭代的 Attribute 实例
将被存储为元组。
示例
>>> from sympy import Symbol
>>> from sympy.codegen.ast import Variable, float32, integer
>>> x = Symbol('x')
>>> v = Variable(x, type=float32)
>>> v.attrs
()
>>> v == Variable('x')
False
>>> v == Variable('x', type=float32)
True
>>> v
Variable(x, type=float32)
也可以通过对符号的假设推断出的类型使用deduced
类方法构造Variable
实例:
>>> i = Symbol('i', integer=True)
>>> v = Variable.deduced(i)
>>> v.type == integer
True
>>> v == Variable('i')
False
>>> from sympy.codegen.ast import value_const
>>> value_const in v.attrs
False
>>> w = Variable('w', attrs=[value_const])
>>> w
Variable(w, attrs=(value_const,))
>>> value_const in w.attrs
True
>>> w.as_Declaration(value=42)
Declaration(Variable(w, value=42, attrs=(value_const,)))
as_Declaration(**kwargs)
创建 Declaration 实例的便利方法。
解释
如果声明的变量需要包装一个修改后的变量关键字参数(例如覆盖变量实例的value
),可以传递。
示例
>>> from sympy.codegen.ast import Variable, NoneToken
>>> x = Variable('x')
>>> decl1 = x.as_Declaration()
>>> # value is special NoneToken() which must be tested with == operator
>>> decl1.variable.value is None # won't work
False
>>> decl1.variable.value == None # not PEP-8 compliant
True
>>> decl1.variable.value == NoneToken() # OK
True
>>> decl2 = x.as_Declaration(value=42.0)
>>> decl2.variable.value == 42.0
True
classmethod deduced(symbol, value=None, attrs=(), cast_check=True)
从Type.from_expr
推断类型的替代构造函数。
主要从symbol
推断类型,次要从value
推断。
参数:
symbol : Symbol
value : expr
(可选)变量的值。
attrs : 可迭代的 Attribute 实例
cast_check : bool
是否在
value
上应用Type.cast_check
。
示例
>>> from sympy import Symbol
>>> from sympy.codegen.ast import Variable, complex_
>>> n = Symbol('n', integer=True)
>>> str(Variable.deduced(n).type)
'integer'
>>> x = Symbol('x', real=True)
>>> v = Variable.deduced(x)
>>> v.type
real
>>> z = Symbol('z', complex=True)
>>> Variable.deduced(z).type == complex_
True
class sympy.codegen.ast.While(*args, **kwargs)
表示代码中的‘for-loop’。
表达式的形式为:
“while condition:
body…”
参数:
condition : 可转换为布尔值的表达式
body : CodeBlock 或可迭代对象
当传递一个可迭代对象时,用于实例化
CodeBlock
。
示例
>>> from sympy import symbols, Gt, Abs
>>> from sympy.codegen import aug_assign, Assignment, While
>>> x, dx = symbols('x dx')
>>> expr = 1 - x**2
>>> whl = While(Gt(Abs(dx), 1e-9), [
... Assignment(dx, -expr/expr.diff(x)),
... aug_assign(x, '+', dx)
... ])
sympy.codegen.ast.aug_assign(lhs, op, rhs)
创建‘lhs op= rhs’。
参数:
lhs : Expr
表示表达式左手边的 SymPy 对象。这些应该是单数对象,如编写代码时所用。显著的类型包括
Symbol
、MatrixSymbol
、MatrixElement
和Indexed
。支持这些类型的子类也被支持。
op : str
运算符(+、-、/、*、%)。
rhs : Expr
表示表达式右手边的 SymPy 对象。这可以是任何类型,只要其形状与左手边对应即可。例如,Matrix 类型可以分配给 MatrixSymbol,但不能分配给 Symbol,因为维度不会对齐。
解释
表示用于代码生成的增强变量赋值。这是一个便利函数。你也可以直接使用增强赋值类,比如AddAugmentedAssignment(x, y)
。
示例
>>> from sympy import symbols
>>> from sympy.codegen.ast import aug_assign
>>> x, y = symbols('x, y')
>>> aug_assign(x, '+', y)
AddAugmentedAssignment(x, y)
``` ## 特殊的 C 数学函数(sympy.codegen.cfunctions)此模块包含 SymPy 函数,与 C 标准库中的特殊数学函数相对应(自 C99 起,也可在 C++11 中使用)。该模块中定义的函数允许用户将`expm1`之类的函数表达为 SymPy 函数,以进行符号操作。```py
class sympy.codegen.cfunctions.Cbrt(*args)
表示立方根函数。
解释
选择Cbrt(x)
而不是cbrt(x)
的原因是后者在内部表示为Pow(x, Rational(1, 3))
,这在进行代码生成时可能不是想要的结果。
示例
>>> from sympy.abc import x
>>> from sympy.codegen.cfunctions import Cbrt
>>> Cbrt(x)
Cbrt(x)
>>> Cbrt(x).diff(x)
1/(3*x**(2/3))
另请参阅
Sqrt
fdiff(argindex=1)
返回此函数的一阶导数。
class sympy.codegen.cfunctions.Sqrt(*args)
表示平方根函数。
解释
为什么应该使用Sqrt(x)
而不是sqrt(x)
的原因是,后者在内部表示为Pow(x, S.Half)
,这可能不符合代码生成时的期望。
示例
>>> from sympy.abc import x
>>> from sympy.codegen.cfunctions import Sqrt
>>> Sqrt(x)
Sqrt(x)
>>> Sqrt(x).diff(x)
1/(2*sqrt(x))
另请参见
Cbrt
fdiff(argindex=1)
返回此函数的一阶导数。
class sympy.codegen.cfunctions.exp2(arg)
代表以二为基数的指数函数。
解释
使用exp2(x)
而不是2**x
的好处在于,在有限精度算术下,后者效率不高。
示例
>>> from sympy.abc import x
>>> from sympy.codegen.cfunctions import exp2
>>> exp2(2).evalf() == 4.0
True
>>> exp2(x).diff(x)
log(2)*exp2(x)
另请参见
log2
fdiff(argindex=1)
返回此函数的一阶导数。
class sympy.codegen.cfunctions.expm1(arg)
代表以自然指数减一的指数函数。
解释
使用expm1(x)
而不是exp(x) - 1
的好处在于,后者在 x 接近零时容易出现取消精度问题。
示例
>>> from sympy.abc import x
>>> from sympy.codegen.cfunctions import expm1
>>> '%.0e' % expm1(1e-99).evalf()
'1e-99'
>>> from math import exp
>>> exp(1e-99) - 1
0.0
>>> expm1(x).diff(x)
exp(x)
另请参见
log1p
fdiff(argindex=1)
返回此函数的一阶导数。
class sympy.codegen.cfunctions.fma(*args)
代表“融合乘加”。
解释
使用fma(x, y, z)
而不是x*y + z
的好处在于,在有限精度算术下,前者受某些 CPU 的特殊指令支持。
示例
>>> from sympy.abc import x, y, z
>>> from sympy.codegen.cfunctions import fma
>>> fma(x, y, z).diff(x)
y
fdiff(argindex=1)
返回此函数的一阶导数。
class sympy.codegen.cfunctions.hypot(*args)
代表求直角三角形斜边长度的函数。
解释
例如,在进行代码生成时,由 C99 标准的数学库提供直角三角形函数,因此可能希望在符号化表示函数时使用该函数。
示例
>>> from sympy.abc import x, y
>>> from sympy.codegen.cfunctions import hypot
>>> hypot(3, 4).evalf() == 5.0
True
>>> hypot(x, y)
hypot(x, y)
>>> hypot(x, y).diff(x)
x/hypot(x, y)
fdiff(argindex=1)
返回此函数的一阶导数。
class sympy.codegen.cfunctions.log10(arg)
代表以十为底数的对数函数。
示例
>>> from sympy.abc import x
>>> from sympy.codegen.cfunctions import log10
>>> log10(100).evalf() == 2.0
True
>>> log10(x).diff(x)
1/(x*log(10))
另请参见
log2
fdiff(argindex=1)
返回此函数的一阶导数。
class sympy.codegen.cfunctions.log1p(arg)
代表对数加一的自然对数。
解释
使用log1p(x)
而不是log(x + 1)
的好处在于,后者在 x 接近零时容易出现取消精度问题。
示例
>>> from sympy.abc import x
>>> from sympy.codegen.cfunctions import log1p
>>> from sympy import expand_log
>>> '%.0e' % expand_log(log1p(1e-99)).evalf()
'1e-99'
>>> from math import log
>>> log(1 + 1e-99)
0.0
>>> log1p(x).diff(x)
1/(x + 1)
另请参见
expm1
fdiff(argindex=1)
返回此函数的一阶导数。
class sympy.codegen.cfunctions.log2(arg)
代表以二为底数的对数函数。
解释
使用log2(x)
而不是log(x)/log(2)
的好处在于,在有限精度算术下,后者效率不高。
示例
>>> from sympy.abc import x
>>> from sympy.codegen.cfunctions import log2
>>> log2(4).evalf() == 2.0
True
>>> log2(x).diff(x)
1/(x*log(2))
另请参见
exp2
, log10
fdiff(argindex=1)
返回此函数的一阶导数。 ## C 特定 AST 节点(sympy.codegen.cnodes)
适用于 C 系语言特定的 AST 节点
class sympy.codegen.cnodes.CommaOperator(*args)
代表 C 语言中的逗号运算符
class sympy.codegen.cnodes.Label(*args, **kwargs)
用于例如 goto 语句的标签。
示例
>>> from sympy import ccode, Symbol
>>> from sympy.codegen.cnodes import Label, PreIncrement
>>> print(ccode(Label('foo')))
foo:
>>> print(ccode(Label('bar', [PreIncrement(Symbol('a'))])))
bar:
++(a);
class sympy.codegen.cnodes.PostDecrement(*args)
代表后自减运算符
示例
>>> from sympy.abc import x
>>> from sympy.codegen.cnodes import PostDecrement
>>> from sympy import ccode
>>> ccode(PostDecrement(x))
'(x)--'
class sympy.codegen.cnodes.PostIncrement(*args)
代表后自增运算符
示例
>>> from sympy.abc import x
>>> from sympy.codegen.cnodes import PostIncrement
>>> from sympy import ccode
>>> ccode(PostIncrement(x))
'(x)++'
class sympy.codegen.cnodes.PreDecrement(*args)
代表前自减运算符
示例
>>> from sympy.abc import x
>>> from sympy.codegen.cnodes import PreDecrement
>>> from sympy import ccode
>>> ccode(PreDecrement(x))
'--(x)'
class sympy.codegen.cnodes.PreIncrement(*args)
代表前自增运算符
示例
>>> from sympy.abc import x
>>> from sympy.codegen.cnodes import PreIncrement
>>> from sympy import ccode
>>> ccode(PreIncrement(x))
'++(x)'
sympy.codegen.cnodes.alignof(arg)
生成调用‘alignof’的 FunctionCall 实例
class sympy.codegen.cnodes.goto(*args, **kwargs)
代表 C 语言中的 goto
sympy.codegen.cnodes.sizeof(arg)
生成调用‘sizeof’的 FunctionCall 实例
示例
>>> from sympy.codegen.ast import real
>>> from sympy.codegen.cnodes import sizeof
>>> from sympy import ccode
>>> ccode(sizeof(real))
'sizeof(double)'
class sympy.codegen.cnodes.struct(*args, **kwargs)
表示 C 中的结构
class sympy.codegen.cnodes.union(*args, **kwargs)
表示 C 中的联合 ## C++特定的 AST 节点(sympy.codegen.cxxnodes)
专用于 C++的 AST 节点。
class sympy.codegen.cxxnodes.using(*args, **kwargs)
表示 C++中的‘using’语句 ## Fortran 特定的 AST 节点(sympy.codegen.fnodes)
专用于 Fortran 的 AST 节点。
该模块中定义的函数允许用户将诸如dsign
之类的函数表达为 SymPy 函数以进行符号操作。
class sympy.codegen.fnodes.ArrayConstructor(*args, **kwargs)
表示一个数组构造器。
示例
>>> from sympy import fcode
>>> from sympy.codegen.fnodes import ArrayConstructor
>>> ac = ArrayConstructor([1, 2, 3])
>>> fcode(ac, standard=95, source_format='free')
'(/1, 2, 3/)'
>>> fcode(ac, standard=2003, source_format='free')
'[1, 2, 3]'
class sympy.codegen.fnodes.Do(*args, **kwargs)
表示 Fortran 中的 Do 循环。
示例
>>> from sympy import fcode, symbols
>>> from sympy.codegen.ast import aug_assign, Print
>>> from sympy.codegen.fnodes import Do
>>> i, n = symbols('i n', integer=True)
>>> r = symbols('r', real=True)
>>> body = [aug_assign(r, '+', 1/i), Print([i, r])]
>>> do1 = Do(body, i, 1, n)
>>> print(fcode(do1, source_format='free'))
do i = 1, nr = r + 1d0/iprint *, i, r
end do
>>> do2 = Do(body, i, 1, n, 2)
>>> print(fcode(do2, source_format='free'))
do i = 1, n, 2r = r + 1d0/iprint *, i, r
end do
class sympy.codegen.fnodes.Extent(*args)
表示一个维度范围。
示例
>>> from sympy.codegen.fnodes import Extent
>>> e = Extent(-3, 3) # -3, -2, -1, 0, 1, 2, 3
>>> from sympy import fcode
>>> fcode(e, source_format='free')
'-3:3'
>>> from sympy.codegen.ast import Variable, real
>>> from sympy.codegen.fnodes import dimension, intent_out
>>> dim = dimension(e, e)
>>> arr = Variable('x', real, attrs=[dim, intent_out])
>>> fcode(arr.as_Declaration(), source_format='free', standard=2003)
'real*8, dimension(-3:3, -3:3), intent(out) :: x'
class sympy.codegen.fnodes.FortranReturn(*args, **kwargs)
明确映射到 Fortran“return”的 AST 节点。
说明
因为 Fortran 中的返回语句与 C 中的不同,并且为了帮助重用我们的代码生成 AST,普通的.codegen.ast.Return
被解释为对函数的结果变量的赋值。如果由于某种原因需要生成一个 Fortran RETURN 语句,应使用此节点。
示例
>>> from sympy.codegen.fnodes import FortranReturn
>>> from sympy import fcode
>>> fcode(FortranReturn('x'))
' return x'
class sympy.codegen.fnodes.GoTo(*args, **kwargs)
表示 Fortran 中的 goto 语句
示例
>>> from sympy.codegen.fnodes import GoTo
>>> go = GoTo([10, 20, 30], 'i')
>>> from sympy import fcode
>>> fcode(go, source_format='free')
'go to (10, 20, 30), i'
class sympy.codegen.fnodes.ImpliedDoLoop(*args, **kwargs)
表示 Fortran 中的隐含 do 循环。
示例
>>> from sympy import Symbol, fcode
>>> from sympy.codegen.fnodes import ImpliedDoLoop, ArrayConstructor
>>> i = Symbol('i', integer=True)
>>> idl = ImpliedDoLoop(i**3, i, -3, 3, 2) # -27, -1, 1, 27
>>> ac = ArrayConstructor([-28, idl, 28]) # -28, -27, -1, 1, 27, 28
>>> fcode(ac, standard=2003, source_format='free')
'[-28, (i**3, i = -3, 3, 2), 28]'
class sympy.codegen.fnodes.Module(*args, **kwargs)
表示 Fortran 中的模块。
示例
>>> from sympy.codegen.fnodes import Module
>>> from sympy import fcode
>>> print(fcode(Module('signallib', ['implicit none'], []), source_format='free'))
module signallib
implicit nonecontainsend module
class sympy.codegen.fnodes.Program(*args, **kwargs)
表示 Fortran 中的‘program’块。
示例
>>> from sympy.codegen.ast import Print
>>> from sympy.codegen.fnodes import Program
>>> prog = Program('myprogram', [Print([42])])
>>> from sympy import fcode
>>> print(fcode(prog, source_format='free'))
program myprogramprint *, 42
end program
class sympy.codegen.fnodes.Subroutine(*args, **kwargs)
表示 Fortran 中的子程序。
示例
>>> from sympy import fcode, symbols
>>> from sympy.codegen.ast import Print
>>> from sympy.codegen.fnodes import Subroutine
>>> x, y = symbols('x y', real=True)
>>> sub = Subroutine('mysub', [x, y], [Print([x**2 + y**2, x*y])])
>>> print(fcode(sub, source_format='free', standard=2003))
subroutine mysub(x, y)
real*8 :: x
real*8 :: y
print *, x**2 + y**2, x*y
end subroutine
class sympy.codegen.fnodes.SubroutineCall(*args, **kwargs)
表示 Fortran 中的子程序调用。
示例
>>> from sympy.codegen.fnodes import SubroutineCall
>>> from sympy import fcode
>>> fcode(SubroutineCall('mysub', 'x y'.split()))
' call mysub(x, y)'
sympy.codegen.fnodes.allocated(array)
为 Fortran 的“allocated(…)”创建一个函数调用的 AST 节点。
示例
>>> from sympy import fcode
>>> from sympy.codegen.fnodes import allocated
>>> alloc = allocated('x')
>>> fcode(alloc, source_format='free')
'allocated(x)'
sympy.codegen.fnodes.array(symbol, dim, intent=None, *, attrs=(), value=None, type=None)
用于为 Fortran 数组创建一个变量实例的便利函数。
参数:
symbol:符号
dim:属性或可迭代对象
如果 dim 是一个
Attribute
,它需要具有名称‘dimension’。如果它不是一个Attribute
,那么它将作为*dim
传递给dimension()
intent:str
其中之一:‘in’、‘out’、‘inout’或无
**kwargs:
Variable
的关键字参数(‘type’和‘value’)
示例
>>> from sympy import fcode
>>> from sympy.codegen.ast import integer, real
>>> from sympy.codegen.fnodes import array
>>> arr = array('a', '*', 'in', type=integer)
>>> print(fcode(arr.as_Declaration(), source_format='free', standard=2003))
integer*4, dimension(*), intent(in) :: a
>>> x = array('x', [3, ':', ':'], intent='out', type=real)
>>> print(fcode(x.as_Declaration(value=1), source_format='free', standard=2003))
real*8, dimension(3, :, :), intent(out) :: x = 1
sympy.codegen.fnodes.bind_C(name=None)
创建一个带有名称的属性bind_C
。
参数:
name:str
示例
>>> from sympy import fcode, Symbol
>>> from sympy.codegen.ast import FunctionDefinition, real, Return
>>> from sympy.codegen.fnodes import array, sum_, bind_C
>>> a = Symbol('a', real=True)
>>> s = Symbol('s', integer=True)
>>> arr = array(a, dim=[s], intent='in')
>>> body = [Return((sum_(a**2)/s)**.5)]
>>> fd = FunctionDefinition(real, 'rms', [arr, s], body, attrs=[bind_C('rms')])
>>> print(fcode(fd, source_format='free', standard=2003))
real*8 function rms(a, s) bind(C, name="rms")
real*8, dimension(s), intent(in) :: a
integer*4 :: s
rms = sqrt(sum(a**2)/s)
end function
class sympy.codegen.fnodes.cmplx(*args)
Fortran 复数转换函数。
sympy.codegen.fnodes.dimension(*args)
创建一个带有(最多 7 个)范围的‘dimension’属性。
示例
>>> from sympy import fcode
>>> from sympy.codegen.fnodes import dimension, intent_in
>>> dim = dimension('2', ':') # 2 rows, runtime determined number of columns
>>> from sympy.codegen.ast import Variable, integer
>>> arr = Variable('a', integer, attrs=[dim, intent_in])
>>> fcode(arr.as_Declaration(), source_format='free', standard=2003)
'integer*4, dimension(2, :), intent(in) :: a'
class sympy.codegen.fnodes.dsign(*args)
用于双精度参数的 Fortran 符号内置函数。
class sympy.codegen.fnodes.isign(*args)
用于整数参数的 Fortran 符号内置函数。
class sympy.codegen.fnodes.kind(*args)
Fortran kind 函数。
sympy.codegen.fnodes.lbound(array, dim=None, kind=None)
为 Fortran 的“lbound(…)”创建一个函数调用的 AST 节点。
参数:
array:符号或字符串
dim:表达式
kind:表达式
示例
>>> from sympy import fcode
>>> from sympy.codegen.fnodes import lbound
>>> lb = lbound('arr', dim=2)
>>> fcode(lb, source_format='free')
'lbound(arr, 2)'
class sympy.codegen.fnodes.literal_dp(num, dps=None, precision=None)
Fortran 双精度实数字面量
class sympy.codegen.fnodes.literal_sp(num, dps=None, precision=None)
Fortran 单精度实数字面量
class sympy.codegen.fnodes.merge(*args)
Fortran 合并函数
sympy.codegen.fnodes.reshape(source, shape, pad=None, order=None)
为 Fortran 的“reshape(…)”创建一个函数调用的 AST 节点。
参数:
source:符号或字符串
shape:ArrayExpr
sympy.codegen.fnodes.shape(source, kind=None)
为 Fortran 的“shape(…)”创建一个函数调用的 AST 节点。
参数:
source:符号或字符串
kind:表达式
示例
>>> from sympy import fcode
>>> from sympy.codegen.fnodes import shape
>>> shp = shape('x')
>>> fcode(shp, source_format='free')
'shape(x)'
sympy.codegen.fnodes.size(array, dim=None, kind=None)
为 Fortran 的“size(…)”创建一个函数调用的 AST 节点。
示例
>>> from sympy import fcode, Symbol
>>> from sympy.codegen.ast import FunctionDefinition, real, Return
>>> from sympy.codegen.fnodes import array, sum_, size
>>> a = Symbol('a', real=True)
>>> body = [Return((sum_(a**2)/size(a))**.5)]
>>> arr = array(a, dim=[':'], intent='in')
>>> fd = FunctionDefinition(real, 'rms', [arr], body)
>>> print(fcode(fd, source_format='free', standard=2003))
real*8 function rms(a)
real*8, dimension(:), intent(in) :: a
rms = sqrt(sum(a**2)*1d0/size(a))
end function
class sympy.codegen.fnodes.use(*args, **kwargs)
表示 Fortran 中的 use 语句。
示例
>>> from sympy.codegen.fnodes import use
>>> from sympy import fcode
>>> fcode(use('signallib'), source_format='free')
'use signallib'
>>> fcode(use('signallib', [('metric', 'snr')]), source_format='free')
'use signallib, metric => snr'
>>> fcode(use('signallib', only=['snr', 'convolution2d']), source_format='free')
'use signallib, only: snr, convolution2d'
class sympy.codegen.fnodes.use_rename(*args, **kwargs)
表示 Fortran 中 use 语句中的重命名。
示例
>>> from sympy.codegen.fnodes import use_rename, use
>>> from sympy import fcode
>>> ren = use_rename("thingy", "convolution2d")
>>> print(fcode(ren, source_format='free'))
thingy => convolution2d
>>> full = use('signallib', only=['snr', ren])
>>> print(fcode(full, source_format='free'))
use signallib, only: snr, thingy => convolution2d
``` ## 算法(sympy.codegen.algorithms)```py
sympy.codegen.algorithms.newtons_method(expr, wrt, atol=1e-12, delta=None, *, rtol=4e-16, debug=False, itermax=None, counter=None, delta_fn=<function <lambda>>, cse=False, handle_nan=None, bounds=None)
为 Newton-Raphson 方法(一种寻根算法)生成 AST。
参数:
expr:表达式
wrt:符号
关于,即变量是什么。
atol:数字或表达式
绝对容差(停止准则)
rtol : 数字或表达式
相对容差(停止准则)
delta : 符号
如果为
None
,将是一个Dummy
。
debug : 布尔值
是否在迭代过程中打印收敛信息
itermax : 数字或表达式
迭代的最大次数。
counter : 符号
如果为
None
,将是一个Dummy
。
delta_fn: Callable[[Expr, Symbol], Expr]
计算步骤,默认为牛顿法。例如,对于 Halley 方法,使用
delta_fn=lambda e, x: -2*e*e.diff(x)/(2*e.diff(x)**2 - e*e.diff(x, 2))
cse: 布尔值
在
delta
表达式上执行常见子表达式消除
handle_nan: 令牌
如何处理非数值(NaN)的出现。
bounds: Optional[tuple[Expr, Expr]]
在界限内执行优化
解释
基于 sympy.codegen.ast
生成的牛顿法根查找的抽象语法树(AST)。
示例
>>> from sympy import symbols, cos
>>> from sympy.codegen.ast import Assignment
>>> from sympy.codegen.algorithms import newtons_method
>>> x, dx, atol = symbols('x dx atol')
>>> expr = cos(x) - x**3
>>> algo = newtons_method(expr, x, atol=atol, delta=dx)
>>> algo.has(Assignment(dx, -expr/expr.diff(x)))
True
参考
[R40]
en.wikipedia.org/wiki/Newton%27s_method
sympy.codegen.algorithms.newtons_method_function(expr, wrt, params=None, func_name='newton', attrs=(), *, delta=None, **kwargs)
生成实现牛顿-拉弗森方法的函数的 AST。
参数:
expr : 表达式
wrt : 符号
关于,即是变量是什么
params : 符号的可迭代对象
在表达式中出现且在迭代过程中被视为常数的符号(这些将被接受为生成函数的参数)。
func_name : 字符串
生成函数的名称。
attrs : 元组
传递给
FunctionDefinition
的attrs
属性实例。
**kwargs :
传递给
sympy.codegen.algorithms.newtons_method()
的关键字参数。
示例
>>> from sympy import symbols, cos
>>> from sympy.codegen.algorithms import newtons_method_function
>>> from sympy.codegen.pyutils import render_as_module
>>> x = symbols('x')
>>> expr = cos(x) - x**3
>>> func = newtons_method_function(expr, x)
>>> py_mod = render_as_module(func) # source code as string
>>> namespace = {}
>>> exec(py_mod, namespace, namespace)
>>> res = eval('newton(0.5)', namespace)
>>> abs(res - 0.865474033102) < 1e-12
True
参见
sympy.codegen.algorithms.newtons_method
## Python 实用工具(sympy.codegen.pyutils)
sympy.codegen.pyutils.render_as_module(content, standard='python3')
将 Python 代码呈现为模块(带有必要的导入)。
参数:
standard :
查看
sympy.printing.pycode.pycode()
中的参数standard
## C 实用工具(sympy.codegen.cutils)
sympy.codegen.cutils.render_as_source_file(content, Printer=<class 'sympy.printing.c.C99CodePrinter'>, settings=None)
渲染为 C 源文件(带有必要的 #include 语句) ## Fortran 实用工具(sympy.codegen.futils)
sympy.codegen.futils.render_as_module(definitions, name, declarations=(), printer_settings=None)
创建 Module
实例并将其呈现为字符串。
这将生成带有正确 use
语句的 Fortran 源代码模块。
参数:
definitions : 可迭代对象
传递给
sympy.codegen.fnodes.Module
。
name : 字符串
传递给
sympy.codegen.fnodes.Module
。
declarations : 可迭代对象
传递给
sympy.codegen.fnodes.Module
。它将通过definitions
生成use
语句、‘implicit none’ 和 public 列表扩展。
printer_settings : 字典
传递给
FCodePrinter
(默认:{'standard': 2003, 'source_format': 'free'}
)。
逻辑
原文:
docs.sympy.org/latest/reference/public/logic/index.html
目录
-
逻辑
-
集合
逻辑
原文链接:
docs.sympy.org/latest/modules/logic.html
介绍
SymPy 的逻辑模块允许使用符号和布尔值形成和操作逻辑表达式。
形成逻辑表达式
您可以使用标准的 Python 操作符&
(And
)、|
(Or
)、~
(Not
)构建布尔表达式:
>>> from sympy import *
>>> x, y = symbols('x,y')
>>> y | (x & y)
y | (x & y)
>>> x | y
x | y
>>> ~x
~x
您还可以使用>>
和<<
形成含义:
>>> x >> y
Implies(x, y)
>>> x << y
Implies(y, x)
像 SymPy 中的大多数类型一样,布尔表达式继承自 Basic
:
>>> (y & x).subs({x: True, y: True})
True
>>> (x | y).atoms()
{x, y}
SymPy 的逻辑模块还包括以下功能,可从其真值表中推导布尔表达式:
sympy.logic.boolalg.SOPform(variables, minterms, dontcares=None)
函数SOPform
使用简化对和冗余组消除算法将产生‘1’的所有输入组合列表(即最小项)转换为最小的“求和乘积”形式。
变量必须作为第一个参数给出。
返回逻辑 Or
函数(即“求和乘积”或“SOP”形式),以达到所需的结果。如果有可以忽略的输入,请也将它们作为列表传递。
结果将是满足条件的函数之一(也许有多个)。
示例
>>> from sympy.logic import SOPform
>>> from sympy import symbols
>>> w, x, y, z = symbols('w x y z')
>>> minterms = [[0, 0, 0, 1], [0, 0, 1, 1],
... [0, 1, 1, 1], [1, 0, 1, 1], [1, 1, 1, 1]]
>>> dontcares = [[0, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 1]]
>>> SOPform([w, x, y, z], minterms, dontcares)
(y & z) | (~w & ~x)
术语也可以表示为整数:
>>> minterms = [1, 3, 7, 11, 15]
>>> dontcares = [0, 2, 5]
>>> SOPform([w, x, y, z], minterms, dontcares)
(y & z) | (~w & ~x)
它们还可以使用字典来指定,不必完全指定:
>>> minterms = [{w: 0, x: 1}, {y: 1, z: 1, x: 0}]
>>> SOPform([w, x, y, z], minterms)
(x & ~w) | (y & z & ~x)
或其组合:
>>> minterms = [4, 7, 11, [1, 1, 1, 1]]
>>> dontcares = [{w : 0, x : 0, y: 0}, 5]
>>> SOPform([w, x, y, z], minterms, dontcares)
(w & y & z) | (~w & ~y) | (x & z & ~w)
参见
POSform
参考文献
[R600]
zh.wikipedia.org/wiki/奎因-麦克拉斯基算法
[R601]
zh.wikipedia.org/wiki/无关紧要的术语
sympy.logic.boolalg.POSform(variables, minterms, dontcares=None)
函数POSform
使用简化对和冗余组消除算法将产生‘1’的所有输入组合列表(即最小项)转换为最小的“乘积求和”形式。
变量必须作为第一个参数给出。
返回逻辑 And
函数(即“乘积求和”或“POS”形式),以达到所需的结果。如果有可以忽略的输入,请也将它们作为列表传递。
结果将是满足条件的函数之一(也许有多个)。
示例
>>> from sympy.logic import POSform
>>> from sympy import symbols
>>> w, x, y, z = symbols('w x y z')
>>> minterms = [[0, 0, 0, 1], [0, 0, 1, 1], [0, 1, 1, 1],
... [1, 0, 1, 1], [1, 1, 1, 1]]
>>> dontcares = [[0, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 1]]
>>> POSform([w, x, y, z], minterms, dontcares)
z & (y | ~w)
术语也可以表示为整数:
>>> minterms = [1, 3, 7, 11, 15]
>>> dontcares = [0, 2, 5]
>>> POSform([w, x, y, z], minterms, dontcares)
z & (y | ~w)
它们还可以使用字典来指定,不必完全指定:
>>> minterms = [{w: 0, x: 1}, {y: 1, z: 1, x: 0}]
>>> POSform([w, x, y, z], minterms)
(x | y) & (x | z) & (~w | ~x)
或其组合:
>>> minterms = [4, 7, 11, [1, 1, 1, 1]]
>>> dontcares = [{w : 0, x : 0, y: 0}, 5]
>>> POSform([w, x, y, z], minterms, dontcares)
(w | x) & (y | ~w) & (z | ~y)
参见
SOPform
参考文献
[R602]
zh.wikipedia.org/wiki/奎因-麦克拉斯基算法
[R603]
zh.wikipedia.org/wiki/无关紧要的术语
sympy.logic.boolalg.ANFform(variables, truthvalues)
ANFform 函数将真值列表转换为代数正常形式(ANF)。
变量必须作为第一个参数给出。
返回True
、False
、逻辑And
函数(即“Zhegalkin 单项式”)或逻辑Xor
函数(即“Zhegalkin 多项式”)。当 True 和 False 分别由 1 和 0 表示时,And
为乘法,Xor
为加法。
严格来说,“Zhegalkin 单项式”是一组不同变量(包括空集)的乘积(逻辑 And),其乘积表示为 1(True)。“Zhegalkin 多项式”是 Zhegalkin 单项式集合的和(逻辑 Xor),其中空集表示为 0(False)。
参数:
变量:变量列表
真值:1 和 0 的列表(真值表的结果列)
示例
>>> from sympy.logic.boolalg import ANFform
>>> from sympy.abc import x, y
>>> ANFform([x], [1, 0])
x ^ True
>>> ANFform([x, y], [0, 1, 1, 1])
x ^ y ^ (x & y)
参考文献
[R604]
zh.wikipedia.org/wiki/Zhegalkin_polynomial
布尔函数
class sympy.logic.boolalg.Boolean(*args)
布尔对象是一种逻辑操作有意义的对象。
as_set()
用实集的术语重写布尔表达式。
示例
>>> from sympy import Symbol, Eq, Or, And
>>> x = Symbol('x', real=True)
>>> Eq(x, 0).as_set()
{0}
>>> (x > 0).as_set()
Interval.open(0, oo)
>>> And(-2 < x, x < 2).as_set()
Interval.open(-2, 2)
>>> Or(x < -2, 2 < x).as_set()
Union(Interval.open(-oo, -2), Interval.open(2, oo))
equals(other)
如果给定的公式具有相同的真值表,则返回True
。要使两个公式相等,它们必须具有相同的文字。
示例
>>> from sympy.abc import A, B, C
>>> from sympy import And, Or, Not
>>> (A >> B).equals(~B >> ~A)
True
>>> Not(And(A, B, C)).equals(And(Not(A), Not(B), Not(C)))
False
>>> Not(And(A, Not(A))).equals(Or(B, Not(B)))
False
class sympy.logic.boolalg.BooleanTrue
SymPy 版本的True
,可以通过S.true
访问的单例。
这是逻辑模块中使用的 SymPy 版本的True
。使用true
而不是True
的主要优势在于,像~
和>>
这样的简写布尔操作在这个类上将按预期工作,而在True
上则按位操作 1。在逻辑模块中,当函数求值为 true 时,它们将返回这个类。
注意
在 SymPy 的各种上下文中,关于何时使用True
和何时使用S.true
可能会有些混淆。重要的是要记住,sympify(True)
返回S.true
。这意味着在大多数情况下,您可以直接使用True
,必要时它会自动转换为S.true
,类似于您通常可以使用 1 代替S.One
。
基本原则是:
“如果所讨论的布尔值可以被任意符号的Boolean
替换,如Or(x, y)
或x > 1
,则使用S.true
。否则,使用True
”
换句话说,仅在布尔用作真实符号表示的情况下使用S.true
。例如,如果对象最终位于任何表达式的.args
中,则必须是S.true
而不是True
,因为.args
的元素必须是Basic
。另一方面,==
在 SymPy 中不是一个符号操作,因为它总是以结构相等性返回True
或False
,而不是数学上的相等性,因此应该返回True
。假设系统应使用True
和False
。除了不满足上述经验法则外,假设系统使用三值逻辑(True
,False
,None
),而S.true
和S.false
表示二值逻辑。当有疑问时,使用True
。
“S.true == True is True
.”
虽然“S.true is True
”是False
,但“S.true == True
”是True
,因此,如果对于函数或表达式是否会返回S.true
或True
存在任何疑问,只需使用==
而不是is
来进行比较,它将在任何情况下都起作用。最后,对于布尔标志,最好只是使用if x
而不是if x is True
。引用 PEP 8:
不要使用==
将布尔值与True
或False
进行比较。
-
是的:
if greeting:
-
不:
if greeting == True:
-
更糟糕的是:
if greeting is True:
举例
>>> from sympy import sympify, true, false, Or
>>> sympify(True)
True
>>> _ is True, _ is true
(False, True)
>>> Or(true, false)
True
>>> _ is true
True
Python 运算符对 true 给出布尔结果,但对 True 给出位结果
>>> ~true, ~True
(False, -2)
>>> true >> true, True >> True
(True, 0)
另请参阅
sympy.logic.boolalg.BooleanFalse
as_set()
重写逻辑运算符和关系运算符以真实集合的术语表示。
举例
>>> from sympy import true
>>> true.as_set()
UniversalSet
class sympy.logic.boolalg.BooleanFalse
SymPy 版本的False
,可以通过S.false
访问的单例。
这是 SymPy 版本的False
,用于逻辑模块中。使用false
而不是False
的主要优势在于,像~
和>>
这样的简便布尔运算符会按预期在这个类上起作用,而对于False
,它们会按位操作 0。在逻辑模块中,当它们评估为 false 时,函数将返回此类。
注释
在sympy.logic.boolalg.BooleanTrue
的注释部分查看笔记
举例
>>> from sympy import sympify, true, false, Or
>>> sympify(False)
False
>>> _ is False, _ is false
(False, True)
>>> Or(true, false)
True
>>> _ is true
True
Python 运算符对 false 给出布尔结果,但对 False 给出位结果
>>> ~false, ~False
(True, -1)
>>> false >> false, False >> False
(True, 0)
另请参阅
sympy.logic.boolalg.BooleanTrue
as_set()
重写逻辑运算符和关系运算符以真实集合的术语表示。
举例
>>> from sympy import false
>>> false.as_set()
EmptySet
class sympy.logic.boolalg.And(*args)
逻辑与功能。
当一个参数为 false 时,按顺序评估其参数,如果它们都为 true,则立即返回 false。
举例
>>> from sympy.abc import x, y
>>> from sympy import And
>>> x & y
x & y
注释
逻辑与操作符&
作为便利性提供,但请注意其在这里的使用与 Python 中的正常用法不同,即它是位与。因此,And(a, b)
和a & b
将在a
和b
为整数时产生不同的结果。
>>> And(x, y).subs(x, 1)
y
class sympy.logic.boolalg.Or(*args)
逻辑或函数
按顺序评估其参数,如果一个参数为真,则立即返回 true,如果它们都为 false,则返回 false。
举例
>>> from sympy.abc import x, y
>>> from sympy import Or
>>> x | y
x | y
注释
|
操作符是为了方便起见提供的,但请注意,它在这里的使用与 Python 中的正常用法不同,Python 中它表示按位或。因此,如果 a
和 b
是整数,Or(a, b)
和 a | b
将返回不同的结果。
>>> Or(x, y).subs(x, 0)
y
class sympy.logic.boolalg.Not(arg)
逻辑 Not 函数(否定)
如果语句为 false
或 False
,则返回 true
;如果语句为 true
或 True
,则返回 false
。
示例
>>> from sympy import Not, And, Or
>>> from sympy.abc import x, A, B
>>> Not(True)
False
>>> Not(False)
True
>>> Not(And(True, False))
True
>>> Not(Or(True, False))
False
>>> Not(And(And(True, x), Or(x, False)))
~x
>>> ~x
~x
>>> Not(And(Or(A, B), Or(~A, ~B)))
~((A | B) & (~A | ~B))
注意
-
~
操作符是为了方便起见提供的,但请注意,它在这里的使用与 Python 中的正常用法不同,Python 中它表示按位取反。特别地,~a
和Not(a)
如果a
是整数将不同。此外,由于 Python 中的布尔值是从int
继承而来,~True
将与~1
相同,即-2
,其布尔值为 True。为避免此问题,请使用 SymPy 的布尔类型true
和false
。 -
在 Python 3.12 中,对 Python 的布尔类型使用位取反操作符
~
已经不推荐使用,并将会发出警告。
>>> from sympy import true
>>> ~True
-2
>>> ~true
False
class sympy.logic.boolalg.Xor(*args)
逻辑 XOR(异或)函数。
如果奇数个参数为 True 而其余为 False,则返回 True。
如果偶数个参数为 True 而其余为 False,则返回 False。
示例
>>> from sympy.logic.boolalg import Xor
>>> from sympy import symbols
>>> x, y = symbols('x y')
>>> Xor(True, False)
True
>>> Xor(True, True)
False
>>> Xor(True, False, True, True, False)
True
>>> Xor(True, False, True, False)
False
>>> x ^ y
x ^ y
注意
^
操作符是为了方便起见提供的,但请注意,它在这里的使用与 Python 中的正常用法不同,Python 中它表示按位异或。特别地,a ^ b
和 Xor(a, b)
如果 a
和 b
是整数将不同。
>>> Xor(x, y).subs(y, 0)
x
class sympy.logic.boolalg.Nand(*args)
逻辑 NAND 函数。
它按顺序评估其参数,如果任何参数为 False,则立即返回 True;如果它们全部为 True,则返回 False。
如果任何参数为 False,则返回 True;如果所有参数为 True,则返回 False。
示例
>>> from sympy.logic.boolalg import Nand
>>> from sympy import symbols
>>> x, y = symbols('x y')
>>> Nand(False, True)
True
>>> Nand(True, True)
False
>>> Nand(x, y)
~(x & y)
class sympy.logic.boolalg.Nor(*args)
逻辑 NOR 函数。
它按顺序评估其参数,如果任何参数为 True,则立即返回 False;如果它们全部为 False,则返回 True。
如果任何参数为 True,则返回 False;如果所有参数为 False,则返回 True。
示例
>>> from sympy.logic.boolalg import Nor
>>> from sympy import symbols
>>> x, y = symbols('x y')
>>> Nor(True, False)
False
>>> Nor(True, True)
False
>>> Nor(False, True)
False
>>> Nor(False, False)
True
>>> Nor(x, y)
~(x | y)
class sympy.logic.boolalg.Xnor(*args)
逻辑 XNOR 函数。
如果奇数个参数为 True 而其余为 False,则返回 False。
如果偶数个参数为 True 而其余为 False,则返回 True。
示例
>>> from sympy.logic.boolalg import Xnor
>>> from sympy import symbols
>>> x, y = symbols('x y')
>>> Xnor(True, False)
False
>>> Xnor(True, True)
True
>>> Xnor(True, False, True, True, False)
False
>>> Xnor(True, False, True, False)
True
class sympy.logic.boolalg.Implies(*args)
逻辑蕴含。
A 蕴含 B 相当于如果 A 则 B。在数学上,它写作 (A \Rightarrow B),相当于 (\neg A \vee B) 或 ~A | B
。
接受两个布尔参数;A 和 B。如果 A 为 True 而 B 为 False,则返回 False;否则返回 True。
示例
>>> from sympy.logic.boolalg import Implies
>>> from sympy import symbols
>>> x, y = symbols('x y')
>>> Implies(True, False)
False
>>> Implies(False, False)
True
>>> Implies(True, True)
True
>>> Implies(False, True)
True
>>> x >> y
Implies(x, y)
>>> y << x
Implies(x, y)
注意
>>
和 <<
操作符是为了方便起见提供的,但请注意,它们在这里的使用与 Python 中的正常用法不同,Python 中它们表示位移。因此,如果 a
和 b
是整数,Implies(a, b)
和 a >> b
将返回不同的结果。特别地,因为 Python 认为 True
和 False
是整数,True >> True
将与 1 >> 1
相同,即 0,其布尔值为 False。为避免此问题,请使用 SymPy 对象 true
和 false
。
>>> from sympy import true, false
>>> True >> False
1
>>> true >> false
False
class sympy.logic.boolalg.Equivalent(*args)
等价关系。
Equivalent(A, B)
当且仅当 A 和 B 都为 True 或都为 False 时为 True。
如果所有参数在逻辑上等价,则返回 True;否则返回 False。
对于两个参数,这相当于Xnor
。
例子
>>> from sympy.logic.boolalg import Equivalent, And
>>> from sympy.abc import x
>>> Equivalent(False, False, False)
True
>>> Equivalent(True, False, False)
False
>>> Equivalent(x, And(x, True))
True
class sympy.logic.boolalg.ITE(*args)
If-then-else 子句。
ITE(A, B, C)
评估并返回 B 的结果,如果 A 为真则返回 C 的结果。所有参数必须是布尔值。
从逻辑门的角度来看,ITE 对应于一个 2 到 1 的多路复用器,其中 A 是选择信号。
例子
>>> from sympy.logic.boolalg import ITE, And, Xor, Or
>>> from sympy.abc import x, y, z
>>> ITE(True, False, True)
False
>>> ITE(Or(True, False), And(True, True), Xor(True, True))
True
>>> ITE(x, y, z)
ITE(x, y, z)
>>> ITE(True, x, y)
x
>>> ITE(False, x, y)
y
>>> ITE(x, y, y)
y
尝试使用非布尔参数将生成一个 TypeError:
>>> ITE(True, [], ())
Traceback (most recent call last):
...
TypeError: expecting bool, Boolean or ITE, not `[]`
class sympy.logic.boolalg.Exclusive(*args)
当只有一个或没有参数为真时为真。
Exclusive(A, B, C)
等效于 ~(A & B) & ~(A & C) & ~(B & C)
。
对于两个参数,这相当于Xor
。
例子
>>> from sympy.logic.boolalg import Exclusive
>>> Exclusive(False, False, False)
True
>>> Exclusive(False, True, False)
True
>>> Exclusive(False, True, True)
False
以下函数可以用来处理代数、合取、析取和否定标准形式:
sympy.logic.boolalg.to_anf(expr, deep=True)
将expr
转换为代数标准形式(ANF)。
ANF 是一个规范的标准形式,这意味着两个等价的公式将转换为相同的 ANF。
逻辑表达式处于 ANF 中,如果它具有以下形式
[1 \oplus a \oplus b \oplus ab \oplus abc]
即可以是:
-
纯粹的真,
-
纯粹的假,
-
变量的合取,
-
互斥的异或。
互斥的异或只能包含真、变量或变量的合取。不允许有否定。
如果deep
是False
,布尔表达式的参数被视为变量,即只有顶层表达式转换为 ANF。
例子
>>> from sympy.logic.boolalg import And, Or, Not, Implies, Equivalent
>>> from sympy.logic.boolalg import to_anf
>>> from sympy.abc import A, B, C
>>> to_anf(Not(A))
A ^ True
>>> to_anf(And(Or(A, B), Not(C)))
A ^ B ^ (A & B) ^ (A & C) ^ (B & C) ^ (A & B & C)
>>> to_anf(Implies(Not(A), Equivalent(B, C)), deep=False)
True ^ ~A ^ (~A & (Equivalent(B, C)))
sympy.logic.boolalg.to_cnf(expr, simplify=False, force=False)
将命题逻辑句子expr
转换为合取范式:((A | ~B | ...) & (B | C | ...) & ...)
。如果simplify
为True
,则使用 Quine-McCluskey 算法将expr
评估为其最简单的 CNF 形式;这可能需要很长时间。如果有超过 8 个变量,则必须将force
标志设置为True
以进行简化(默认为False
)。
例子
>>> from sympy.logic.boolalg import to_cnf
>>> from sympy.abc import A, B, D
>>> to_cnf(~(A | B) | D)
(D | ~A) & (D | ~B)
>>> to_cnf((A | B) & (A | ~A), True)
A | B
sympy.logic.boolalg.to_dnf(expr, simplify=False, force=False)
将命题逻辑句子expr
转换为析取范式:((A & ~B & ...) | (B & C & ...) | ...)
。如果simplify
为True
,则使用 Quine-McCluskey 算法将expr
评估为其最简单的 DNF 形式;这可能需要很长时间。如果有超过 8 个变量,则必须将force
标志设置为True
以进行简化(默认为False
)。
例子
>>> from sympy.logic.boolalg import to_dnf
>>> from sympy.abc import A, B, C
>>> to_dnf(B & (A | C))
(A & B) | (B & C)
>>> to_dnf((A & B) | (A & ~B) | (B & C) | (~B & C), True)
A | C
sympy.logic.boolalg.to_nnf(expr, simplify=True)
将expr
转换为否定标准形式(NNF)。
逻辑表达式处于 NNF 中,如果它仅包含And
、Or
和Not
,并且Not
仅应用于文字。如果simplify
为True
,结果不包含多余的子句。
例子
>>> from sympy.abc import A, B, C, D
>>> from sympy.logic.boolalg import Not, Equivalent, to_nnf
>>> to_nnf(Not((~A & ~B) | (C & D)))
(A | B) & (~C | ~D)
>>> to_nnf(Equivalent(A >> B, B >> A))
(A | ~B | (A & ~B)) & (B | ~A | (B & ~A))
sympy.logic.boolalg.is_anf(expr)
检查expr
是否在代数标准形式(ANF)中。
逻辑表达式处于 ANF 中,如果它具有以下形式
[1 \oplus a \oplus b \oplus ab \oplus abc]
即它是纯粹的真、纯粹的假、变量的合取或互斥的异或。互斥的异或只能包含真、变量或变量的合取。不允许有否定。
例子
>>> from sympy.logic.boolalg import And, Not, Xor, true, is_anf
>>> from sympy.abc import A, B, C
>>> is_anf(true)
True
>>> is_anf(A)
True
>>> is_anf(And(A, B, C))
True
>>> is_anf(Xor(A, Not(B)))
False
sympy.logic.boolalg.is_cnf(expr)
测试表达式是否处于合取范式中。
例子
>>> from sympy.logic.boolalg import is_cnf
>>> from sympy.abc import A, B, C
>>> is_cnf(A | B | C)
True
>>> is_cnf(A & B & C)
True
>>> is_cnf((A & B) | C)
False
sympy.logic.boolalg.is_dnf(expr)
测试表达式是否为析取范式(DNF)。
示例
>>> from sympy.logic.boolalg import is_dnf
>>> from sympy.abc import A, B, C
>>> is_dnf(A | B | C)
True
>>> is_dnf(A & B & C)
True
>>> is_dnf((A & B) | C)
True
>>> is_dnf(A & (B | C))
False
sympy.logic.boolalg.is_nnf(expr, simplified=True)
检查expr
是否处于否定范式(NNF)。
逻辑表达式在 NNF 中,如果只包含And
、Or
和Not
,并且Not
仅应用于文字。如果simplified
为True
,则检查结果是否不包含冗余子句。
示例
>>> from sympy.abc import A, B, C
>>> from sympy.logic.boolalg import Not, is_nnf
>>> is_nnf(A & B | ~C)
True
>>> is_nnf((A | ~A) & (B | C))
False
>>> is_nnf((A | ~A) & (B | C), False)
True
>>> is_nnf(Not(A & B) | C)
False
>>> is_nnf((A >> B) & (B >> A))
False
sympy.logic.boolalg.gateinputcount(expr)
返回实现布尔表达式的逻辑门的总输入数。
返回:
整数
门输入数
注意
此处只有标准门才算作布尔函数,包括:And
、Or
、Xor
、Not
和ITE
(多路复用器)。Nand
、Nor
和Xnor
将被计算为Not(And())
等。
示例
>>> from sympy.logic import And, Or, Nand, Not, gateinputcount
>>> from sympy.abc import x, y, z
>>> expr = And(x, y)
>>> gateinputcount(expr)
2
>>> gateinputcount(Or(expr, z))
4
注意,Nand
自动计算为Not(And())
,因此
>>> gateinputcount(Nand(x, y, z))
4
>>> gateinputcount(Not(And(x, y, z)))
4
尽管可以通过使用evaluate=False
来避免此问题
>>> gateinputcount(Nand(x, y, z, evaluate=False))
3
还要注意,比较将计为布尔变量:
>>> gateinputcount(And(x > z, y >= 2))
2
如符号:>>> gateinputcount(x) 0
简化和等价测试
sympy.logic.boolalg.simplify_logic(expr, form=None, deep=True, force=False, dontcare=None)
此函数将布尔函数简化为其标准形式中的简化版本(SOP 或 POS)。返回类型是 SymPy 中的Or
或And
对象。
参数:
expr:布尔值
form:字符串('cnf'
或'dnf'
)或None
(默认)。
如果是
'cnf'
或'dnf'
,则返回相应正规形式中的最简表达式;如果是None
,则根据参数最少的形式返回答案(默认为 CNF)。
deep:布尔值(默认True
)
指示是否递归简化输入中包含的任何非布尔函数。
force:布尔值(默认False
)
由于简化在变量数量的指数时间内,对具有 8 个以上变量的表达式默认有一个限制。当表达式超过 8 个变量时,只进行符号化简化(由
deep
控制)。通过将force
设置为True
,可以移除此限制。请注意,这可能导致非常长的简化时间。
dontcare:布尔值
在假设这个表达式为真的输入是不重要的情况下优化表达式。例如,在分段条件中很有用,后续条件不需要考虑前面条件转换的输入。例如,如果前面的条件是
And(A, B)
,则可以使用对And(A, B)
的不重要性简化表达式。
示例
>>> from sympy.logic import simplify_logic
>>> from sympy.abc import x, y, z
>>> b = (~x & ~y & ~z) | ( ~x & ~y & z)
>>> simplify_logic(b)
~x & ~y
>>> simplify_logic(x | y, dontcare=y)
x
参考文献
[R605]
不重要术语
SymPy 的simplify()
函数也可用于将逻辑表达式简化为其最简形式。
sympy.logic.boolalg.bool_map(bool1, bool2)
返回bool1的简化版本,以及使两个表达式bool1和bool2在变量之间的某种对应关系下表示相同逻辑行为的变量映射。如果存在多个这样的映射,则返回其中一个。
例如,对于映射{x: a, y: b}
或{x: b, y: a}
,And(x, y)
在逻辑上等价于And(a, b)
。如果不存在这样的映射,则返回False
。
示例
>>> from sympy import SOPform, bool_map, Or, And, Not, Xor
>>> from sympy.abc import w, x, y, z, a, b, c, d
>>> function1 = SOPform([x, z, y],[[1, 0, 1], [0, 0, 1]])
>>> function2 = SOPform([a, b, c],[[1, 0, 1], [1, 0, 0]])
>>> bool_map(function1, function2)
(y & ~z, {y: a, z: b})
结果不一定是唯一的,但它们是规范的。这里,(w, z)
可以是(a, d)
或(d, a)
:
>>> eq = Or(And(Not(y), w), And(Not(y), z), And(x, y))
>>> eq2 = Or(And(Not(c), a), And(Not(c), d), And(b, c))
>>> bool_map(eq, eq2)
((x & y) | (w & ~y) | (z & ~y), {w: a, x: b, y: c, z: d})
>>> eq = And(Xor(a, b), c, And(c,d))
>>> bool_map(eq, eq.subs(c, x))
(c & d & (a | b) & (~a | ~b), {a: a, b: b, c: d, d: x})
操纵表达式
可以用来操作布尔表达式的以下函数:
sympy.logic.boolalg.distribute_and_over_or(expr)
给定一个由文字的合取和析取组成的句子expr
,返回一个等价的 CNF 形式的句子。
示例
>>> from sympy.logic.boolalg import distribute_and_over_or, And, Or, Not
>>> from sympy.abc import A, B, C
>>> distribute_and_over_or(Or(A, And(Not(B), Not(C))))
(A | ~B) & (A | ~C)
sympy.logic.boolalg.distribute_or_over_and(expr)
给定一个由文字的合取和析取组成的句子expr
,返回一个等价的 DNF 形式的句子。
注意输出未简化。
示例
>>> from sympy.logic.boolalg import distribute_or_over_and, And, Or, Not
>>> from sympy.abc import A, B, C
>>> distribute_or_over_and(And(Or(Not(A), B), C))
(B & C) | (C & ~A)
sympy.logic.boolalg.distribute_xor_over_and(expr)
给定一个由文字的合取和排他析取组成的句子expr
,返回一个等价的排他析取。
注意输出未简化。
示例
>>> from sympy.logic.boolalg import distribute_xor_over_and, And, Xor, Not
>>> from sympy.abc import A, B, C
>>> distribute_xor_over_and(And(Xor(Not(A), B), C))
(B & C) ^ (C & ~A)
sympy.logic.boolalg.eliminate_implications(expr)
将Implies
和Equivalent
改为And
,Or
和Not
。也就是说,返回一个与expr
等价的表达式,但只使用&
、|
和~
作为逻辑运算符。
示例
>>> from sympy.logic.boolalg import Implies, Equivalent, eliminate_implications
>>> from sympy.abc import A, B, C
>>> eliminate_implications(Implies(A, B))
B | ~A
>>> eliminate_implications(Equivalent(A, B))
(A | ~B) & (B | ~A)
>>> eliminate_implications(Equivalent(A, B, C))
(A | ~C) & (B | ~A) & (C | ~B)
真值表及相关函数
可以创建布尔函数的真值表。
sympy.logic.boolalg.truth_table(expr, variables, input=True)
返回输入变量的所有可能配置的生成器,以及这些值的布尔表达式的结果。
参数:
expr:布尔表达式
variables:变量列表
input:布尔值(默认为True
)
指示是否返回输入组合。
示例
>>> from sympy.logic.boolalg import truth_table
>>> from sympy.abc import x,y
>>> table = truth_table(x >> y, [x, y])
>>> for t in table:
... print('{0} -> {1}'.format(*t))
[0, 0] -> True
[0, 1] -> True
[1, 0] -> False
[1, 1] -> True
>>> table = truth_table(x | y, [x, y])
>>> list(table)
[([0, 0], False), ([0, 1], True), ([1, 0], True), ([1, 1], True)]
如果input
为False
,truth_table
仅返回一个真值列表。在这种情况下,可以从给定输出的索引推导出变量的相应输入值。
>>> from sympy.utilities.iterables import ibin
>>> vars = [y, x]
>>> values = truth_table(x >> y, vars, input=False)
>>> values = list(values)
>>> values
[True, False, True, True]
>>> for i, value in enumerate(values):
... print('{0} -> {1}'.format(list(zip(
... vars, ibin(i, len(vars)))), value))
[(y, 0), (x, 0)] -> True
[(y, 0), (x, 1)] -> False
[(y, 1), (x, 0)] -> True
[(y, 1), (x, 1)] -> True
为了在真值表位置的整数表示、零和一的列表以及符号之间进行映射,可以使用以下函数:
sympy.logic.boolalg.integer_to_term(n, bits=None, str=False)
返回长度为bits
的列表,该列表对应于表示n
的二进制值,小位数在右边(最后)。如果省略 bits,则长度将是表示n
所需的位数。如果希望以反向顺序显示位数,请使用返回列表的[::-1]
切片。
如果希望得到从[0, 0,..., 0]
到[1, 1, ..., 1]
的所有位长度列表的序列,则传递非整数作为位数,例如 'all'
。
如果需要比特串,则传递str=True
。
示例
>>> from sympy.utilities.iterables import ibin
>>> ibin(2)
[1, 0]
>>> ibin(2, 4)
[0, 0, 1, 0]
如果希望所有与 0 到\(2^n - 1\)对应的列表,请传递非整数作为位数:
>>> bits = 2
>>> for i in ibin(2, 'all'):
... print(i)
(0, 0)
(0, 1)
(1, 0)
(1, 1)
如果需要给定长度的比特串,请使用 str=True:
>>> n = 123
>>> bits = 10
>>> ibin(n, bits, str=True)
'0001111011'
>>> ibin(n, bits, str=True)[::-1] # small bits left
'1101111000'
>>> list(ibin(3, 'all', str=True))
['000', '001', '010', '011', '100', '101', '110', '111']
sympy.logic.boolalg.term_to_integer(term)
返回与项给定的基于二进制的 2 进制数相对应的整数。
参数:
项:字符串或 1 和 0 列表
示例
>>> from sympy.logic.boolalg import term_to_integer
>>> term_to_integer([1, 0, 0])
4
>>> term_to_integer('100')
4
sympy.logic.boolalg.bool_maxterm(k, variables)
返回第 k 个最大项。
每个最大项根据用于最小项的传统相反的二进制编码而分配一个索引。最大项的约定将直接形式分配值 0,补码形式分配值 1。
参数:
k:整数或 1 和 0 列表(补码模式)
变量:变量列表
示例
>>> from sympy.logic.boolalg import bool_maxterm
>>> from sympy.abc import x, y, z
>>> bool_maxterm([1, 0, 1], [x, y, z])
y | ~x | ~z
>>> bool_maxterm(6, [x, y, z])
z | ~x | ~y
参考文献
[R606]
zh.wikipedia.org/wiki/正规范式#最大项的索引
sympy.logic.boolalg.bool_minterm(k, variables)
返回第 k 个最小项。
最小项按变量的补码模式的二进制编码编号。此约定将直接形式分配值 1,补码形式分配值 0。
参数:
k:整数或 1 和 0 列表(补码模式)
变量:变量列表
示例
>>> from sympy.logic.boolalg import bool_minterm
>>> from sympy.abc import x, y, z
>>> bool_minterm([1, 0, 1], [x, y, z])
x & z & ~y
>>> bool_minterm(6, [x, y, z])
x & y & ~z
参考文献
[R607]
zh.wikipedia.org/wiki/正规范式#最小项的索引
sympy.logic.boolalg.bool_monomial(k, variables)
返回第 k 个单项式。
单项式按变量的存在和不存在的二进制编码编号。此约定将变量的存在分配值 1,变量的不存在分配值 0。
每个布尔函数都可以通过 Zhegalkin 多项式(代数正常形式)唯一表示。具有\(n\)个变量的布尔函数的 Zhegalkin 多项式可以包含多达\(2^n\)个单项式。我们可以枚举所有的单项式。每个单项式由每个变量的存在或不存在来完全指定。
例如,具有四个变量(a, b, c, d)
的布尔函数可以包含多达\(2⁴ = 16\)个单项式。第 13 个单项式是乘积a & b & d
,因为 13 在二进制中是 1, 1, 0, 1。
参数:
k:整数或 1 和 0 列表
变量:变量列表
示例
>>> from sympy.logic.boolalg import bool_monomial
>>> from sympy.abc import x, y, z
>>> bool_monomial([1, 0, 1], [x, y, z])
x & z
>>> bool_monomial(6, [x, y, z])
x & y
sympy.logic.boolalg.anf_coeffs(truthvalues)
将某个布尔表达式的真值列表转换为在 ANF 中表示该布尔表达式的多项式模 2 的系数列表(排他或)(即“Zhegalkin 多项式”)。
在(n)个变量中,Zhegalkin 单项式有(2^n)个可能,因为每个单项式通过每个变量的有无来完全指定。
我们可以枚举所有的单项式。例如,具有四个变量(a, b, c, d)
的布尔函数可以包含最多(2⁴ = 16)个单项式。第 13 个单项式是乘积a & b & d
,因为 13 在二进制中是 1, 1, 0, 1。
一个给定单项式在多项式中的出现与该单项式的系数分别为 1 或 0 相对应。
例子
>>> from sympy.logic.boolalg import anf_coeffs, bool_monomial, Xor
>>> from sympy.abc import a, b, c
>>> truthvalues = [0, 1, 1, 0, 0, 1, 0, 1]
>>> coeffs = anf_coeffs(truthvalues)
>>> coeffs
[0, 1, 1, 0, 0, 0, 1, 0]
>>> polynomial = Xor(*[
... bool_monomial(k, [a, b, c])
... for k, coeff in enumerate(coeffs) if coeff == 1
... ])
>>> polynomial
b ^ c ^ (a & b)
sympy.logic.boolalg.to_int_repr(clauses, symbols)
将 CNF 格式的子句转换为整数表示。
例子
>>> from sympy.logic.boolalg import to_int_repr
>>> from sympy.abc import x, y
>>> to_int_repr([x | y, y], [x, y]) == [{1, 2}, {2}]
True
推断
该模块实现了命题逻辑中的一些推断例程。
函数satisfiable
将测试给定的布尔表达式是否可满足,即你可以为变量分配值使得句子为True
。
例如,表达式x & ~x
是不可满足的,因为没有任何值可以使得这个句子为True
。另一方面,(x | y) & (x | ~y) & (~x | y)
是可满足的,其中x
和y
都为True
。
>>> from sympy.logic.inference import satisfiable
>>> from sympy import Symbol
>>> x = Symbol('x')
>>> y = Symbol('y')
>>> satisfiable(x & ~x)
False
>>> satisfiable((x | y) & (x | ~y) & (~x | y))
{x: True, y: True}
正如你所见,当一个句子是可满足的时候,它会返回一个使得这个句子为True
的模型。如果它不可满足,则会返回False
。
sympy.logic.inference.satisfiable(expr, algorithm=None, all_models=False, minimal=False, use_lra_theory=False)
检查命题句子的可满足性。当成功时返回一个模型。对于显然为真的表达式,返回{true: true}
。
当将all_models
设置为 True 时,如果给定的表达式是可满足的,则返回一个模型的生成器。然而,如果表达式是不可满足的,则返回一个包含单一元素False
的生成器。
例子
>>> from sympy.abc import A, B
>>> from sympy.logic.inference import satisfiable
>>> satisfiable(A & ~B)
{A: True, B: False}
>>> satisfiable(A & ~A)
False
>>> satisfiable(True)
{True: True}
>>> next(satisfiable(A & ~A, all_models=True))
False
>>> models = satisfiable((A >> B) & B, all_models=True)
>>> next(models)
{A: False, B: True}
>>> next(models)
{A: True, B: True}
>>> def use_models(models):
... for model in models:
... if model:
... # Do something with the model.
... print(model)
... else:
... # Given expr is unsatisfiable.
... print("UNSAT")
>>> use_models(satisfiable(A >> ~A, all_models=True))
{A: False}
>>> use_models(satisfiable(A ^ A, all_models=True))
UNSAT