SymPy 1.13 中文文档(二十六)
N 维数组表达式
原文链接:
docs.sympy.org/latest/modules/tensor/array_expressions.html
数组表达式是表示 N 维数组的表达式,而不对它们进行评估。这些表达式以某种方式表示对 N 维数组的操作的抽象语法树。
每个 N 维数组运算符都有对应的数组表达式对象。
对应表:
| Array operator | Array expression operator |
|---|---|
| tensorproduct | ArrayTensorProduct |
| tensorcontraction | ArrayContraction |
| tensordiagonal | ArrayDiagonal |
| permutedims | PermuteDims |
示例
ArraySymbol 对象是矩阵模块中 MatrixSymbol 对象的 N 维等效物。
>>> from sympy.tensor.array.expressions import ArraySymbol
>>> from sympy.abc import i, j, k
>>> A = ArraySymbol("A", (3, 2, 4))
>>> A.shape
(3, 2, 4)
>>> A[i, j, k]
A[i, j, k]
>>> A.as_explicit()
[[[A[0, 0, 0], A[0, 0, 1], A[0, 0, 2], A[0, 0, 3]],
[A[0, 1, 0], A[0, 1, 1], A[0, 1, 2], A[0, 1, 3]]],
[[A[1, 0, 0], A[1, 0, 1], A[1, 0, 2], A[1, 0, 3]],
[A[1, 1, 0], A[1, 1, 1], A[1, 1, 2], A[1, 1, 3]]],
[[A[2, 0, 0], A[2, 0, 1], A[2, 0, 2], A[2, 0, 3]],
[A[2, 1, 0], A[2, 1, 1], A[2, 1, 2], A[2, 1, 3]]]]
在数组表达式中可以添加组件明确的数组:
>>> from sympy import Array
>>> from sympy import tensorproduct
>>> from sympy.tensor.array.expressions import ArrayTensorProduct
>>> a = Array([1, 2, 3])
>>> b = Array([i, j, k])
>>> expr = ArrayTensorProduct(a, b, b)
>>> expr
ArrayTensorProduct([1, 2, 3], [i, j, k], [i, j, k])
>>> expr.as_explicit() == tensorproduct(a, b, b)
True
从索引明确形式构建数组表达式
数组表达式是索引隐式的。这意味着它们不使用任何索引来表示数组操作。函数 convert_indexed_to_array( ... ) 可以用来将索引明确的表达式转换为数组表达式。它接受两个参数作为输入:索引明确表达式和索引的顺序:
>>> from sympy.tensor.array.expressions import convert_indexed_to_array
>>> from sympy import Sum
>>> A = ArraySymbol("A", (3, 3))
>>> B = ArraySymbol("B", (3, 3))
>>> convert_indexed_to_array(A[i, j], [i, j])
A
>>> convert_indexed_to_array(A[i, j], [j, i])
PermuteDims(A, (0 1))
>>> convert_indexed_to_array(A[i, j] + B[j, i], [i, j])
ArrayAdd(A, PermuteDims(B, (0 1)))
>>> convert_indexed_to_array(Sum(A[i, j]*B[j, k], (j, 0, 2)), [i, k])
ArrayContraction(ArrayTensorProduct(A, B), (1, 2))
矩阵的数组表达式形式的对角线:
>>> convert_indexed_to_array(A[i, i], [i])
ArrayDiagonal(A, (0, 1))
矩阵的数组表达式形式的迹:
>>> convert_indexed_to_array(Sum(A[i, i], (i, 0, 2)), [i])
ArrayContraction(A, (0, 1))
与矩阵的兼容性
数组表达式可以与矩阵模块中的对象混合使用:
>>> from sympy import MatrixSymbol
>>> from sympy.tensor.array.expressions import ArrayContraction
>>> M = MatrixSymbol("M", 3, 3)
>>> N = MatrixSymbol("N", 3, 3)
在数组表达式形式中表示矩阵乘积:
>>> from sympy.tensor.array.expressions import convert_matrix_to_array
>>> expr = convert_matrix_to_array(M*N)
>>> expr
ArrayContraction(ArrayTensorProduct(M, N), (1, 2))
可以将表达式转换回矩阵形式:
>>> from sympy.tensor.array.expressions import convert_array_to_matrix
>>> convert_array_to_matrix(expr)
M*N
在剩余的轴上添加第二次收缩以获得 (M \cdot N) 的迹:
>>> expr_tr = ArrayContraction(expr, (0, 1))
>>> expr_tr
ArrayContraction(ArrayContraction(ArrayTensorProduct(M, N), (1, 2)), (0, 1))
通过调用 .doit() 展开表达式并移除嵌套的数组收缩操作:
>>> expr_tr.doit()
ArrayContraction(ArrayTensorProduct(M, N), (0, 3), (1, 2))
获取数组表达式的显式形式:
>>> expr.as_explicit()
[[M[0, 0]*N[0, 0] + M[0, 1]*N[1, 0] + M[0, 2]*N[2, 0], M[0, 0]*N[0, 1] + M[0, 1]*N[1, 1] + M[0, 2]*N[2, 1], M[0, 0]*N[0, 2] + M[0, 1]*N[1, 2] + M[0, 2]*N[2, 2]],
[M[1, 0]*N[0, 0] + M[1, 1]*N[1, 0] + M[1, 2]*N[2, 0], M[1, 0]*N[0, 1] + M[1, 1]*N[1, 1] + M[1, 2]*N[2, 1], M[1, 0]*N[0, 2] + M[1, 1]*N[1, 2] + M[1, 2]*N[2, 2]],
[M[2, 0]*N[0, 0] + M[2, 1]*N[1, 0] + M[2, 2]*N[2, 0], M[2, 0]*N[0, 1] + M[2, 1]*N[1, 1] + M[2, 2]*N[2, 1], M[2, 0]*N[0, 2] + M[2, 1]*N[1, 2] + M[2, 2]*N[2, 2]]]
在数组表达式形式中表示矩阵的迹:
>>> from sympy import Trace
>>> convert_matrix_to_array(Trace(M))
ArrayContraction(M, (0, 1))
>>> convert_matrix_to_array(Trace(M*N))
ArrayContraction(ArrayTensorProduct(M, N), (0, 3), (1, 2))
表示矩阵的转置(将表达为轴的排列):
>>> convert_matrix_to_array(M.T)
PermuteDims(M, (0 1))
计算导数数组表达式:
>>> from sympy.tensor.array.expressions import array_derive
>>> d = array_derive(M, M)
>>> d
PermuteDims(ArrayTensorProduct(I, I), (3)(1 2))
验证导数是否与使用明确矩阵计算的形式相对应:
>>> d.as_explicit()
[[[[1, 0, 0], [0, 0, 0], [0, 0, 0]], [[0, 1, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 1], [0, 0, 0], [0, 0, 0]]], [[[0, 0, 0], [1, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 1, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 1], [0, 0, 0]]], [[[0, 0, 0], [0, 0, 0], [1, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 1, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 1]]]]
>>> Me = M.as_explicit()
>>> Me.diff(Me)
[[[[1, 0, 0], [0, 0, 0], [0, 0, 0]], [[0, 1, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 1], [0, 0, 0], [0, 0, 0]]], [[[0, 0, 0], [1, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 1, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 1], [0, 0, 0]]], [[[0, 0, 0], [0, 0, 0], [1, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 1, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 1]]]]
class sympy.tensor.array.expressions.ArrayTensorProduct(*args, **kwargs)
用于表示数组类对象的张量积的类。
class sympy.tensor.array.expressions.ArrayContraction(expr, *contraction_indices, **kwargs)
这个类用来表示数组在代码打印机易于处理的形式中的收缩。
class sympy.tensor.array.expressions.ArrayDiagonal(expr, *diagonal_indices, **kwargs)
用于表示对角线操作符的类。
解释
在二维数组中返回对角线,这看起来像是这样的操作:
(A_{ij} \rightarrow A_{ii})
两个二维数组 (A \otimes B) 的张量积的轴 1 和 2(第二和第三)的对角线是
(\Big[ A_{ab} B_{cd} \Big]{abcd} \rightarrow \Big[ A{ai} B_{id} \Big]_{adi})
在这个最后的例子中,数组表达式已从 4 维降至 3 维。请注意,没有进行收缩,而是对对角线引入了新的索引 (i),收缩会将数组降至 2 维。
注意,对角化的维度会添加为新的维度放在索引的末尾。
class sympy.tensor.array.expressions.PermuteDims(expr, permutation=None, index_order_old=None, index_order_new=None, **kwargs)
用于表示数组轴的排列的类。
示例
>>> from sympy.tensor.array import permutedims
>>> from sympy import MatrixSymbol
>>> M = MatrixSymbol("M", 3, 3)
>>> cg = permutedims(M, [1, 0])
对象 cg 表示 M 的转置,如排列 [1, 0] 将通过交换其索引作用于它:
(M_{ij} \Rightarrow M_{ji})
当转换回矩阵形式时,这一点显而易见:
>>> from sympy.tensor.array.expressions.from_array_to_matrix import convert_array_to_matrix
>>> convert_array_to_matrix(cg)
M.T
>>> N = MatrixSymbol("N", 3, 2)
>>> cg = permutedims(N, [1, 0])
>>> cg.shape
(2, 3)
有可选参数可作为排列的替代品使用:
>>> from sympy.tensor.array.expressions import ArraySymbol, PermuteDims
>>> M = ArraySymbol("M", (1, 2, 3, 4, 5))
>>> expr = PermuteDims(M, index_order_old="ijklm", index_order_new="kijml")
>>> expr
PermuteDims(M, (0 2 1)(3 4))
>>> expr.shape
(3, 1, 2, 5, 4)
张量积的排列被简化,以达到标准形式:
>>> from sympy.tensor.array import tensorproduct
>>> M = MatrixSymbol("M", 4, 5)
>>> tp = tensorproduct(M, N)
>>> tp.shape
(4, 5, 3, 2)
>>> perm1 = permutedims(tp, [2, 3, 1, 0])
参数 (M, N) 已经排序并简化了排列,表达式等效:
>>> perm1.expr.args
(N, M)
>>> perm1.shape
(3, 2, 5, 4)
>>> perm1.permutation
(2 3)
数组形式的排列已从 [2, 3, 1, 0] 简化为 [0, 1, 3, 2],因为张量积 (M) 和 (N) 的参数已经交换:
>>> perm1.permutation.array_form
[0, 1, 3, 2]
我们可以嵌套第二个排列:
>>> perm2 = permutedims(perm1, [1, 0, 2, 3])
>>> perm2.shape
(2, 3, 5, 4)
>>> perm2.permutation.array_form
[1, 0, 3, 2]
索引对象
定义索引对象的模块。
类IndexedBase、Indexed和Idx表示矩阵元素M[i, j]如下图所示:
1) The Indexed class represents the entire indexed object.
|
___|___
' '
M[i, j]
/ \__\______
| |
| |
| 2) The Idx class represents indices; each Idx can
| optionally contain information about its range.
|
3) IndexedBase represents the 'stem' of an indexed object, here `M`.
The stem used by itself is usually taken to represent the entire
array.
Indexed对象上可以有任意数量的索引。这些基础对象中未实现任何转换属性,但支持重复索引的隐式收缩。
注意,对于复杂(即非原子)整数表达式作为索引的支持有限。(在未来版本中应进行改进。)
示例
要表示上述矩阵元素示例,您可以这样写:
>>> from sympy import symbols, IndexedBase, Idx
>>> M = IndexedBase('M')
>>> i, j = symbols('i j', cls=Idx)
>>> M[i, j]
M[i, j]
乘积中的重复索引意味着求和,因此要使用Indexed对象来表示矩阵向量乘积:
>>> x = IndexedBase('x')
>>> M[i, j]*x[j]
M[i, j]*x[j]
如果索引对象将转换为基于组件的数组,例如使用代码打印机或自动包装框架,则还需要提供(符号或数值)维度。可以通过在构建IndexedBase时传递可选的形状参数来完成:
>>> dim1, dim2 = symbols('dim1 dim2', integer=True)
>>> A = IndexedBase('A', shape=(dim1, 2*dim1, dim2))
>>> A.shape
(dim1, 2*dim1, dim2)
>>> A[i, j, 3].shape
(dim1, 2*dim1, dim2)
如果IndexedBase对象没有形状信息,则假定数组大小与其索引的范围一样大:
>>> n, m = symbols('n m', integer=True)
>>> i = Idx('i', m)
>>> j = Idx('j', n)
>>> M[i, j].shape
(m, n)
>>> M[i, j].ranges
[(0, m - 1), (0, n - 1)]
可与以下进行比较:
>>> A[i, 2, j].shape
(dim1, 2*dim1, dim2)
>>> A[i, 2, j].ranges
[(0, m - 1), None, (0, n - 1)]
要分析索引表达式的结构,可以使用get_indices()和get_contraction_structure()方法:
>>> from sympy.tensor import get_indices, get_contraction_structure
>>> get_indices(A[i, j, j])
({i}, {})
>>> get_contraction_structure(A[i, j, j])
{(j,): {A[i, j, j]}}
查看相应的文档字符串以详细了解输出。
class sympy.tensor.indexed.Idx(label, range=None, **kw_args)
表示整数索引作为Integer或整数表达式。
有多种方法可以创建Idx对象。构造函数接受两个参数:
label
整数或符号用于标记索引。
range
可选地可以将范围指定为
-
Symbol或整数:被解释为维度。下界和上界分别设置为0和range - 1。 -
tuple:两个元素分别解释为范围的下限和上限。
注意:范围的边界假定为整数或无穷大(允许使用 oo 和-oo 指定无界范围)。如果边界给定为n,则n.is_integer不能返回 false。
为方便起见,如果标签是字符串,则自动转换为整数符号。(注意:不会对范围或维度参数进行此转换。)
示例
>>> from sympy import Idx, symbols, oo
>>> n, i, L, U = symbols('n i L U', integer=True)
如果标签是字符串,则创建一个整数Symbol,且边界都是None:
>>> idx = Idx('qwerty'); idx
qwerty
>>> idx.lower, idx.upper
(None, None)
可以指定上界和下界:
>>> idx = Idx(i, (L, U)); idx
i
>>> idx.lower, idx.upper
(L, U)
当只给出单个边界时,它被解释为维度,下界默认为 0:
>>> idx = Idx(i, n); idx.lower, idx.upper
(0, n - 1)
>>> idx = Idx(i, 4); idx.lower, idx.upper
(0, 3)
>>> idx = Idx(i, oo); idx.lower, idx.upper
(0, oo)
property label
返回Idx对象的标签(整数或整数表达式)。
示例
>>> from sympy import Idx, Symbol
>>> x = Symbol('x', integer=True)
>>> Idx(x).label
x
>>> j = Symbol('j', integer=True)
>>> Idx(j).label
j
>>> Idx(j + 1).label
j + 1
property lower
返回Idx的下界。
示例
>>> from sympy import Idx
>>> Idx('j', 2).lower
0
>>> Idx('j', 5).lower
0
>>> Idx('j').lower is None
True
property upper
返回Idx的上界。
示例
>>> from sympy import Idx
>>> Idx('j', 2).upper
1
>>> Idx('j', 5).upper
4
>>> Idx('j').upper is None
True
class sympy.tensor.indexed.Indexed(base, *args, **kw_args)
表示具有索引的数学对象。
>>> from sympy import Indexed, IndexedBase, Idx, symbols
>>> i, j = symbols('i j', cls=Idx)
>>> Indexed('A', i, j)
A[i, j]
建议通过索引IndexedBase创建Indexed对象:IndexedBase('A')[i, j]而不是Indexed(IndexedBase('A'), i, j)。
>>> A = IndexedBase('A')
>>> a_ij = A[i, j] # Prefer this,
>>> b_ij = Indexed(A, i, j) # over this.
>>> a_ij == b_ij
True
property base
返回Indexed对象的IndexedBase。
示例
>>> from sympy import Indexed, IndexedBase, Idx, symbols
>>> i, j = symbols('i j', cls=Idx)
>>> Indexed('A', i, j).base
A
>>> B = IndexedBase('B')
>>> B == B[i, j].base
True
property indices
返回 Indexed 对象的索引。
示例
>>> from sympy import Indexed, Idx, symbols
>>> i, j = symbols('i j', cls=Idx)
>>> Indexed('A', i, j).indices
(i, j)
property ranges
返回带有每个索引的下限和上限范围的元组列表。
如果索引未定义数据成员的上限和下限,则列表中的相应位置包含None而不是元组。
示例
>>> from sympy import Indexed,Idx, symbols
>>> Indexed('A', Idx('i', 2), Idx('j', 4), Idx('k', 8)).ranges
[(0, 1), (0, 3), (0, 7)]
>>> Indexed('A', Idx('i', 3), Idx('j', 3), Idx('k', 3)).ranges
[(0, 2), (0, 2), (0, 2)]
>>> x, y, z = symbols('x y z', integer=True)
>>> Indexed('A', x, y, z).ranges
[None, None, None]
property rank
返回 Indexed 对象的秩。
示例
>>> from sympy import Indexed, Idx, symbols
>>> i, j, k, l, m = symbols('i:m', cls=Idx)
>>> Indexed('A', i, j).rank
2
>>> q = Indexed('A', i, j, k, l, m)
>>> q.rank
5
>>> q.rank == len(q.indices)
True
property shape
返回每个索引的维度列表。
维度是数组的属性,而不是索引的属性。但是,如果 IndexedBase 未定义形状属性,则假定索引的范围对应于数组的形状。
>>> from sympy import IndexedBase, Idx, symbols
>>> n, m = symbols('n m', integer=True)
>>> i = Idx('i', m)
>>> j = Idx('j', m)
>>> A = IndexedBase('A', shape=(n, n))
>>> B = IndexedBase('B')
>>> A[i, j].shape
(n, n)
>>> B[i, j].shape
(m, m)
class sympy.tensor.indexed.IndexedBase(label, shape=None, *, offset=0, strides=None, **kw_args)
表示索引对象的基础或干扰
IndexedBase 类表示一个包含元素的数组。该类的主要目的是允许方便地创建 Indexed 类的对象。IndexedBase 的__getitem__方法返回 Indexed 的实例。单独使用,即没有索引,IndexedBase 类可以用作例如矩阵方程的标记,类似于使用 Symbol 类可以做的事情。但是,IndexedBase 类增加了 Symbol 实例不可用的功能:
IndexedBase对象可以选择性地存储形状信息。这可用于检查数组的一致性和 numpy 广播的条件。(TODO)IndexedBase对象实现了语法糖,允许使用重复索引的隐式求和来轻松表示数组操作。IndexedBase对象象征着一个数学结构,相当于数组,因此被用于代码生成和自动编译和包装。
>>> from sympy.tensor import IndexedBase, Idx
>>> from sympy import symbols
>>> A = IndexedBase('A'); A
A
>>> type(A)
<class 'sympy.tensor.indexed.IndexedBase'>
当 IndexedBase 对象接收到索引时,它返回一个带有命名轴的数组,由 Indexed 对象表示:
>>> i, j = symbols('i j', integer=True)
>>> A[i, j, 2]
A[i, j, 2]
>>> type(A[i, j, 2])
<class 'sympy.tensor.indexed.Indexed'>
IndexedBase 构造函数接受一个可选的形状参数。如果给定,则会覆盖索引中的任何形状信息。(但不覆盖索引范围!)
>>> m, n, o, p = symbols('m n o p', integer=True)
>>> i = Idx('i', m)
>>> j = Idx('j', n)
>>> A[i, j].shape
(m, n)
>>> B = IndexedBase('B', shape=(o, p))
>>> B[i, j].shape
(o, p)
假设可以与关键字参数一起指定,方式与 Symbol 相同:
>>> A_real = IndexedBase('A', real=True)
>>> A_real.is_real
True
>>> A != A_real
True
假设也可以通过使用 Symbol 初始化 IndexedBase 来继承:
>>> I = symbols('I', integer=True)
>>> C_inherit = IndexedBase(I)
>>> C_explicit = IndexedBase('I', integer=True)
>>> C_inherit == C_explicit
True
property label
返回 IndexedBase 对象的标签。
示例
>>> from sympy import IndexedBase
>>> from sympy.abc import x, y
>>> IndexedBase('A', shape=(x, y)).label
A
property offset
返回 IndexedBase 对象的偏移量。
当将 2D Indexed 对象展开为 1D 形式时,添加到结果索引的值。用于代码生成。
示例
>>> from sympy.printing import ccode
>>> from sympy.tensor import IndexedBase, Idx
>>> from sympy import symbols
>>> l, m, n, o = symbols('l m n o', integer=True)
>>> A = IndexedBase('A', strides=(l, m, n), offset=o)
>>> i, j, k = map(Idx, 'ijk')
>>> ccode(A[i, j, k])
'A[l*i + m*j + n*k + o]'
property shape
返回 IndexedBase 对象的形状。
示例
>>> from sympy import IndexedBase, Idx
>>> from sympy.abc import x, y
>>> IndexedBase('A', shape=(x, y)).shape
(x, y)
注意:如果指定了 IndexedBase 的形状,它将覆盖索引给出的任何形状信息。
>>> A = IndexedBase('A', shape=(x, y))
>>> B = IndexedBase('B')
>>> i = Idx('i', 2)
>>> j = Idx('j', 1)
>>> A[i, j].shape
(x, y)
>>> B[i, j].shape
(2, 1)
property strides
返回 IndexedBase 对象的步进方案。
通常,这是一个元组,表示遍历数组时在相应维度上要采取的步数。为了代码生成的目的,也可以使用 strides='C' 和 strides='F'。
strides='C' 意味着代码打印器将按行主序展开,而'F'表示按列主序展开。
方法
原文链接:
docs.sympy.org/latest/modules/tensor/index_methods.html
包含对 IndexedBase、Indexed 和 Idx 对象操作的模块
-
检查形状符合度
-
确定结果表达式中的索引
等等。
此模块中的方法可以通过调用 Expr 对象上的方法来实现。当事物稳定下来时,这可能是一个有用的重构。
sympy.tensor.index_methods.get_contraction_structure(expr)
确定expr的虚指数并描述其结构
通过dummy,我们指的是求和索引。
表达式的结构如下确定并描述:
-
描述了 Indexed 对象的符合求和,其中键是求和索引,相应的值是所有适用求和的项的集合。SymPy 表达式树中的所有 Add 对象都是这样描述的。
-
对于 SymPy 表达式树中所有不是 Add 类型的节点,适用以下规则:
如果节点发现其参数中有缩并,则该节点本身将作为字典中的一个键存储。对于该键,相应的值是一个字典列表,每个字典是对 get_contraction_structure()递归调用的结果。该列表仅包含非平凡深层次缩并的字典,省略了只有一个键为 None 的字典。
注意
字典键中包含的表达式表示了多级索引缩并。嵌套字典显示了嵌套缩并,并可能包含来自更深层级的字典。在实际计算中,必须首先计算最深层嵌套级别的求和,以便外部表达式可以访问生成的索引对象。
示例
>>> from sympy.tensor.index_methods import get_contraction_structure
>>> from sympy import default_sort_key
>>> from sympy.tensor import IndexedBase, Idx
>>> x, y, A = map(IndexedBase, ['x', 'y', 'A'])
>>> i, j, k, l = map(Idx, ['i', 'j', 'k', 'l'])
>>> get_contraction_structure(x[i]*y[i] + A[j, j])
{(i,): {x[i]*y[i]}, (j,): {A[j, j]}}
>>> get_contraction_structure(x[i]*y[j])
{None: {x[i]*y[j]}}
缩并因子的乘积导致表示内部缩并的嵌套字典。
>>> d = get_contraction_structure(x[i, i]*y[j, j])
>>> sorted(d.keys(), key=default_sort_key)
[None, x[i, i]*y[j, j]]
在这种情况下,产品没有缩并:
>>> d[None]
{x[i, i]*y[j, j]}
因子首先进行缩并:
>>> sorted(d[x[i, i]*y[j, j]], key=default_sort_key)
[{(i,): {x[i, i]}}, {(j,): {y[j, j]}}]
带括号的 Add 对象也作为嵌套字典返回。括号内的项是包含参数之间缩并的 Mul,因此它将作为结果中的键。它存储了对 Add 表达式进行递归调用后得到的字典。
>>> d = get_contraction_structure(x[i]*(y[i] + A[i, j]*x[j]))
>>> sorted(d.keys(), key=default_sort_key)
[(A[i, j]*x[j] + y[i])*x[i], (i,)]
>>> d[(i,)]
{(A[i, j]*x[j] + y[i])*x[i]}
>>> d[x[i]*(A[i, j]*x[j] + y[i])]
[{None: {y[i]}, (j,): {A[i, j]*x[j]}}]
在底数或指数中具有缩并的幂也将作为字典中的键,映射到来自递归调用的结果列表:
>>> d = get_contraction_structure(A[j, j]**A[i, i])
>>> d[None]
{A[j, j]**A[i, i]}
>>> nested_contractions = d[A[j, j]**A[i, i]]
>>> nested_contractions[0]
{(j,): {A[j, j]}}
>>> nested_contractions[1]
{(i,): {A[i, i]}}
上述示例中用字符串表示的缩并结构描述可能看起来很复杂,但迭代处理起来很容易:
>>> from sympy import Expr
>>> for key in d:
... if isinstance(key, Expr):
... continue
... for term in d[key]:
... if term in d:
... # treat deepest contraction first
... pass
... # treat outermost contactions here
sympy.tensor.index_methods.get_indices(expr)
确定表达式expr的外部索引。
通过outer,我们指的是非求和索引。返回一个集合和一个字典。集合包含外部索引,字典包含索引对称性的信息。
示例
>>> from sympy.tensor.index_methods import get_indices
>>> from sympy import symbols
>>> from sympy.tensor import IndexedBase
>>> x, y, A = map(IndexedBase, ['x', 'y', 'A'])
>>> i, j, a, z = symbols('i j a z', integer=True)
确定总表达式的索引,重复的索引意味着求和,例如矩阵 A 的迹:
>>> get_indices(A[i, i])
(set(), {})
在多项式情况下,要求项具有相同的外部索引。否则将引发 IndexConformanceException 异常。
>>> get_indices(x[i] + A[i, j]*y[j])
({i}, {})
异常:
IndexConformanceException 表示术语不兼容,例如。
>>> get_indices(x[i] + y[j])
(...)
IndexConformanceException: Indices are not consistent: x(i) + y(j)
警告
外部指数的概念递归应用,从最深层开始。这意味着括号内部的虚数被假定首先求和,以便优雅地处理以下表达式:
>>> get_indices((x[i] + A[i, j]*y[j])*x[j])
({i, j}, {})
这是正确的,可能看起来方便,但你需要小心,因为如果要求,SymPy 会愉快地.expand()这个乘积。结果表达式将混合外部的j与括号内部的虚数,使其成为不同的表达式。为了安全起见,最好通过为所有应分开的收缩使用唯一的指数来避免这种模棱两可的情况。
张量
class sympy.tensor.tensor.TensorIndexType(name, dummy_name=None, dim=None, eps_dim=None, metric_symmetry=1, metric_name='metric', **kwargs)
张量指标类型由其名称和度规确定。
参数:
name:张量类型的名称
dummy_name:虚指标的头部名称
dim:维度,可以是符号、整数或 None
eps_dim:epsilon 张量的维度
metric_symmetry:表示度规对称性的整数或 None 表示无度规
metric_name:度规张量的名称字符串
注意
metric_symmetry 参数的可能值为:
1:度规张量完全对称0:度规张量没有指标对称性-1:度规张量完全反对称None:没有度规张量(度规等于None)
默认情况下,度规假定为对称的。也可以通过 .set_metric() 方法设置自定义张量。
如果有度规,则使用度规来提升和降低指标。
在非对称度规的情况下,将采用以下提升和降低约定:
psi(a) = g(a, b)*psi(-b); chi(-a) = chi(b)*g(-b, -a)
由此可以轻松找到:
g(-a, b) = delta(-a, b)
其中 delta(-a, b) = delta(b, -a) 是 Kronecker delta(参见 TensorIndex 关于指标约定)。对于反对称度规,还有以下等式:
g(a, -b) = -delta(a, -b)
如果没有度规,则无法提升或降低指标;例如,SU(N) 的定义表示的指标是“协变的”,共轭表示是“逆变的”;对于 N > 2,它们是线性独立的。
如果 dim 是整数,则 eps_dim 默认等于 dim;否则可以分配(用于简单的尺寸正规化);如果 eps_dim 不是整数,则 epsilon 为 None。
示例
>>> from sympy.tensor.tensor import TensorIndexType
>>> Lorentz = TensorIndexType('Lorentz', dummy_name='L')
>>> Lorentz.metric
metric(Lorentz,Lorentz)
属性
metric | (度规张量) |
|---|---|
delta | (Kronecker delta) |
epsilon | (Levi-Civita epsilon 张量) |
data | ((已弃用) 用于在指定基础上添加 ndarray 值的属性) |
class sympy.tensor.tensor.TensorIndex(name, tensor_index_type, is_up=True)
表示张量指标
参数:
name:指标的名称,或者 True 表示自动分配
tensor_index_type:指标的 TensorIndexType
is_up:逆变指标的标志(默认为 True)
注意
张量指标遵循爱因斯坦求和约定进行缩并。
指标可以是逆变形式或协变形式;在后一种情况下,索引名称前加 -。向协变(is_up=False)索引添加 - 使其变为逆变。
虚指标的名称默认为 tensor_inde_type.dummy_name,后跟下划线和数字。
类似于 symbols,可以使用 tensor_indices(s, typ) 一次创建多个逆变指标,其中 s 是名称字符串。
示例
>>> from sympy.tensor.tensor import TensorIndexType, TensorIndex, TensorHead, tensor_indices
>>> Lorentz = TensorIndexType('Lorentz', dummy_name='L')
>>> mu = TensorIndex('mu', Lorentz, is_up=False)
>>> nu, rho = tensor_indices('nu, rho', Lorentz)
>>> A = TensorHead('A', [Lorentz, Lorentz])
>>> A(mu, nu)
A(-mu, nu)
>>> A(-mu, -rho)
A(mu, -rho)
>>> A(mu, -mu)
A(-L_0, L_0)
属性
name | |
|---|---|
tensor_index_type | |
is_up |
class sympy.tensor.tensor.TensorHead(name, index_types, symmetry=None, comm=0)
张量的张量头。
参数:
name:张量的名称
index_types:TensorIndexType 的列表
symmetry:张量的 TensorSymmetry
comm:对易群号
注意事项
与symbols类似,可以使用tensorhead(s, typ, sym=None, comm=0)函数创建多个TensorHead,其中s是名称的字符串,sym是单项张量对称性(参见tensorsymmetry)。
TensorHead属于一个对易群,由符号和数字comm定义(参见_TensorManager.set_comm);对易群中的张量具有相同的对易性质;默认情况下,comm为0,表示对易张量的群。
示例
定义一个完全反对称的二阶张量:
>>> from sympy.tensor.tensor import TensorIndexType, TensorHead, TensorSymmetry
>>> Lorentz = TensorIndexType('Lorentz', dummy_name='L')
>>> asym2 = TensorSymmetry.fully_symmetric(-2)
>>> A = TensorHead('A', [Lorentz, Lorentz], asym2)
示例中使用 ndarray 值,假定分配给TensorHead对象的组件数据处于完全逆变表示。如果需要分配表示非完全协变张量值的组件数据,请参阅其他示例。
>>> from sympy.tensor.tensor import tensor_indices
>>> from sympy import diag
>>> Lorentz = TensorIndexType('Lorentz', dummy_name='L')
>>> i0, i1 = tensor_indices('i0:2', Lorentz)
指定一个替换字典以跟踪在张量表达式中使用的数组进行替换。TensorIndexType与用于缩并的度规相关联(以完全协变形式):
>>> repl = {Lorentz: diag(1, -1, -1, -1)}
让我们看一些与电磁张量的组件一起工作的示例:
>>> from sympy import symbols
>>> Ex, Ey, Ez, Bx, By, Bz = symbols('E_x E_y E_z B_x B_y B_z')
>>> c = symbols('c', positive=True)
让我们定义(F),一个反对称张量:
>>> F = TensorHead('F', [Lorentz, Lorentz], asym2)
让我们更新字典,包含用于替换的矩阵:
>>> repl.update({F(-i0, -i1): [
... [0, Ex/c, Ey/c, Ez/c],
... [-Ex/c, 0, -Bz, By],
... [-Ey/c, Bz, 0, -Bx],
... [-Ez/c, -By, Bx, 0]]})
现在可以检索电磁张量的逆变形式:
>>> F(i0, i1).replace_with_arrays(repl, [i0, i1])
[[0, -E_x/c, -E_y/c, -E_z/c], [E_x/c, 0, -B_z, B_y], [E_y/c, B_z, 0, -B_x], [E_z/c, -B_y, B_x, 0]]
和混合的逆变-协变形式:
>>> F(i0, -i1).replace_with_arrays(repl, [i0, -i1])
[[0, E_x/c, E_y/c, E_z/c], [E_x/c, 0, B_z, -B_y], [E_y/c, -B_z, 0, B_x], [E_z/c, B_y, -B_x, 0]]
粒子的能量-动量可以表示为:
>>> from sympy import symbols
>>> P = TensorHead('P', [Lorentz], TensorSymmetry.no_symmetry(1))
>>> E, px, py, pz = symbols('E p_x p_y p_z', positive=True)
>>> repl.update({P(i0): [E, px, py, pz]})
分别是逆变和协变分量:
>>> P(i0).replace_with_arrays(repl, [i0])
[E, p_x, p_y, p_z]
>>> P(-i0).replace_with_arrays(repl, [-i0])
[E, -p_x, -p_y, -p_z]
1-索引张量的收缩:
>>> expr = P(i0)*P(-i0)
>>> expr.replace_with_arrays(repl, [])
E**2 - p_x**2 - p_y**2 - p_z**2
属性
name | |
|---|---|
index_types | |
rank | (索引的总数) |
symmetry | |
comm | (对易群) |
commutes_with(other)
如果self和other对易,则返回0,如果它们反对易,则返回1。
如果self和other既不对易也不反对易,则返回None。
sympy.tensor.tensor.tensor_heads(s, index_types, symmetry=None, comm=0)
从字符串(s)返回一系列TensorHead
class sympy.tensor.tensor.TensExpr(*args)
张量表达式的抽象基类
注意事项
张量表达式是由张量形成的表达式;目前将张量的和分布开来。
TensExpr可以是TensAdd或TensMul。
TensMul对象由分量张量的乘积组成,并包括一个系数,这是一个 SymPy 表达式。
在内部表示中,收缩的指标由(ipos1, ipos2, icomp1, icomp2)表示,其中icomp1是具有逆变指标的分量张量的位置,ipos1是该分量张量中指标所占的插槽。
因此,在内部表示中,收缩的指标是无名的。
get_matrix()
已弃用:请勿使用。
如果组件数据可用且 ndarray 维度不超过 2,则返回 ndarray 组件数据作为矩阵。
replace_with_arrays(replacement_dict, indices=None)
用数组替换张量表达式。最终的数组将对应于按照indices排列的 N 维数组。
参数:
replacement_dict
包含张量替换规则的字典。
indices
与该数组读取相关的索引顺序。如果未传递任何值,则将使用原始索引顺序。
示例
>>> from sympy.tensor.tensor import TensorIndexType, tensor_indices
>>> from sympy.tensor.tensor import TensorHead
>>> from sympy import symbols, diag
>>> L = TensorIndexType("L")
>>> i, j = tensor_indices("i j", L)
>>> A = TensorHead("A", [L])
>>> A(i).replace_with_arrays({A(i): [1, 2]}, [i])
[1, 2]
由于‘indices’是可选的,因此如果不需要特定的索引顺序,我们也可以通过这种方式调用 replace_with_arrays:
>>> A(i).replace_with_arrays({A(i): [1, 2]})
[1, 2]
>>> expr = A(i)*A(j)
>>> expr.replace_with_arrays({A(i): [1, 2]})
[[1, 2], [2, 4]]
对于缩并,指定TensorIndexType的度量L的协变形式:
>>> expr = A(i)*A(-i)
>>> expr.replace_with_arrays({A(i): [1, 2], L: diag(1, -1)})
-3
数组的对称化:
>>> H = TensorHead("H", [L, L])
>>> a, b, c, d = symbols("a b c d")
>>> expr = H(i, j)/2 + H(j, i)/2
>>> expr.replace_with_arrays({H(i, j): [[a, b], [c, d]]})
[[a, b/2 + c/2], [b/2 + c/2, d]]
反对称化的数组:
>>> expr = H(i, j)/2 - H(j, i)/2
>>> repl = {H(i, j): [[a, b], [c, d]]}
>>> expr.replace_with_arrays(repl)
[[0, b/2 - c/2], [-b/2 + c/2, 0]]
同一表达式也可以读作通过反转i和j来进行转置:
>>> expr.replace_with_arrays(repl, [j, i])
[[0, -b/2 + c/2], [b/2 - c/2, 0]]
class sympy.tensor.tensor.TensAdd(*args, **kw_args)
张量的和。
参数:
free_args:自由指标的列表
示例
>>> from sympy.tensor.tensor import TensorIndexType, tensor_heads, tensor_indices
>>> Lorentz = TensorIndexType('Lorentz', dummy_name='L')
>>> a, b = tensor_indices('a,b', Lorentz)
>>> p, q = tensor_heads('p,q', [Lorentz])
>>> t = p(a) + q(a); t
p(a) + q(a)
示例,将组件数据添加到张量表达式中:
>>> from sympy import symbols, diag
>>> x, y, z, t = symbols("x y z t")
>>> repl = {}
>>> repl[Lorentz] = diag(1, -1, -1, -1)
>>> repl[p(a)] = [1, 2, 3, 4]
>>> repl[q(a)] = [x, y, z, t]
以下是:22 - 32 - 22 - 72 ==> -58
>>> expr = p(a) + q(a)
>>> expr.replace_with_arrays(repl, [a])
[x + 1, y + 2, z + 3, t + 4]
属性
args | (加法因子的元组) |
|---|---|
rank | (张量的秩) |
free_args | (按排序顺序列出的自由指标的列表) |
canon_bp()
使用 Butler-Portugal 算法进行单项对称性下的规范化。
contract_metric(g)
使用度量g升降指标。
参数:
g:度量
contract_all:如果为真,则消除所有已缩并的g。
注释
请参见TensorIndexType的文档字符串以获取缩并约定。
class sympy.tensor.tensor.TensMul(*args, **kw_args)
张量的乘积。
参数:
coeff:张量的 SymPy 系数
args
注释
args[0]:组分张量的TensorHead的列表。
args[1]:(索引,位置,分量)的列表,其中ind是自由指标,ipos是icomp-th 分量张量中ind的插槽位置。
args[2]:表示虚指标的元组列表。(ipos1, ipos2, icomp1, icomp2)指示协变虚指标在icomp1-th 组分张量的第ipos1个插槽位置;相应的逆变指标在icomp2-th 组分张量的第ipos2个插槽位置。
属性
components | (组分张量的TensorHead的列表) |
|---|---|
types | (非重复的TensorIndexType的列表) |
free | (索引,位置,分量)的列表,请参见注释。 |
dum | (ipos1,ipos2,icomp1,icomp2)的列表,请参见注释。 |
ext_rank | (计算虚指标的张量秩) |
rank | (张量的秩) |
coeff | (张量的 SymPy 系数) |
free_args | (按排序顺序列出的自由指标的列表) |
is_canon_bp | (如果张量处于规范形式则为True) |
canon_bp()
使用 Butler-Portugal 算法进行单项对称性下的规范化。
示例
>>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, TensorHead, TensorSymmetry
>>> Lorentz = TensorIndexType('Lorentz', dummy_name='L')
>>> m0, m1, m2 = tensor_indices('m0,m1,m2', Lorentz)
>>> A = TensorHead('A', [Lorentz]*2, TensorSymmetry.fully_symmetric(-2))
>>> t = A(m0,-m1)*A(m1,-m0)
>>> t.canon_bp()
-A(L_0, L_1)*A(-L_0, -L_1)
>>> t = A(m0,-m1)*A(m1,-m2)*A(m2,-m0)
>>> t.canon_bp()
0
contract_metric(g)
使用度量g升降指标。
参数:
g:度量
注释
请参见TensorIndexType的文档字符串以获取缩并约定。
示例
>>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensor_heads
>>> Lorentz = TensorIndexType('Lorentz', dummy_name='L')
>>> m0, m1, m2 = tensor_indices('m0,m1,m2', Lorentz)
>>> g = Lorentz.metric
>>> p, q = tensor_heads('p,q', [Lorentz])
>>> t = p(m0)*q(m1)*g(-m0, -m1)
>>> t.canon_bp()
metric(L_0, L_1)*p(-L_0)*q(-L_1)
>>> t.contract_metric(g).canon_bp()
p(L_0)*q(-L_0)
get_free_indices() → list[TensorIndex]
返回张量的自由指标列表。
解释
索引按组分张量中出现的顺序列出。
示例
>>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensor_heads
>>> Lorentz = TensorIndexType('Lorentz', dummy_name='L')
>>> m0, m1, m2 = tensor_indices('m0,m1,m2', Lorentz)
>>> g = Lorentz.metric
>>> p, q = tensor_heads('p,q', [Lorentz])
>>> t = p(m1)*g(m0,m2)
>>> t.get_free_indices()
[m1, m0, m2]
>>> t2 = p(m1)*g(-m1, m2)
>>> t2.get_free_indices()
[m2]
get_indices()
返回张量的索引列表。
解释
索引按组分张量中出现的顺序列出。虚指标被赋予一个不会与自由指标名称冲突的名称。
示例
>>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensor_heads
>>> Lorentz = TensorIndexType('Lorentz', dummy_name='L')
>>> m0, m1, m2 = tensor_indices('m0,m1,m2', Lorentz)
>>> g = Lorentz.metric
>>> p, q = tensor_heads('p,q', [Lorentz])
>>> t = p(m1)*g(m0,m2)
>>> t.get_indices()
[m1, m0, m2]
>>> t2 = p(m1)*g(-m1, m2)
>>> t2.get_indices()
[L_0, -L_0, m2]
perm2tensor(g, is_canon_bp=False)
返回与排列g对应的张量。
更多详细信息,请参见 TIDS 中具有相同名称的方法。
sorted_components()
返回一个具有排序组件的张量积。
split()
返回一个张量列表,其乘积为 self。
解释
不同张量组件之间的虚指标被用来表示相同名称的自由指标。
示例
>>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, tensor_heads, TensorSymmetry
>>> Lorentz = TensorIndexType('Lorentz', dummy_name='L')
>>> a, b, c, d = tensor_indices('a,b,c,d', Lorentz)
>>> A, B = tensor_heads('A,B', [Lorentz]*2, TensorSymmetry.fully_symmetric(2))
>>> t = A(a,b)*B(-b,c)
>>> t
A(a, L_0)*B(-L_0, c)
>>> t.split()
[A(a, L_0), B(-L_0, c)]
sympy.tensor.tensor.canon_bp(p)
巴特勒-葡萄牙规范化。详见组合学模块的 tensor_can.py。
sympy.tensor.tensor.riemann_cyclic_replace(t_r)
将黎曼张量替换为等效表达式。
R(m,n,p,q) -> 2/3*R(m,n,p,q) - 1/3*R(m,q,n,p) + 1/3*R(m,p,n,q)
sympy.tensor.tensor.riemann_cyclic(t2)
用满足循环恒等式的等效表达式替换每个黎曼张量。
这个技巧在 Cadabra 参考指南中讨论过。
示例
>>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, TensorHead, riemann_cyclic, TensorSymmetry
>>> Lorentz = TensorIndexType('Lorentz', dummy_name='L')
>>> i, j, k, l = tensor_indices('i,j,k,l', Lorentz)
>>> R = TensorHead('R', [Lorentz]*4, TensorSymmetry.riemann())
>>> t = R(i,j,k,l)*(R(-i,-j,-k,-l) - 2*R(-i,-k,-j,-l))
>>> riemann_cyclic(t)
0
class sympy.tensor.tensor.TensorSymmetry(*args, **kw_args)
张量的单项对称性(即任何对称或反对称的索引置换)。有关相关术语,请参阅组合学模块的 tensor_can.py 部分。
参数:
bsgs:元组 (base, sgs) 张量的对称性的 BSGS
注释
一个张量可以通过其 BSGS 提供任意单项对称性。多项对称性,如黎曼张量的循环对称性(即比安基恒等式),不包括在内。有关如何生成一般索引置换群的 BSGS 的信息,请参见组合学模块。可以使用内置方法生成简单的对称性。
示例
定义一个二阶对称张量
>>> from sympy.tensor.tensor import TensorIndexType, TensorSymmetry, get_symmetric_group_sgs, TensorHead
>>> Lorentz = TensorIndexType('Lorentz', dummy_name='L')
>>> sym = TensorSymmetry(get_symmetric_group_sgs(2))
>>> T = TensorHead('T', [Lorentz]*2, sym)
注意,使用内置的 TensorSymmetry 方法也可以完成相同的工作
>>> sym2 = TensorSymmetry.fully_symmetric(2)
>>> sym == sym2
True
另请参阅
sympy.combinatorics.tensor_can.get_symmetric_group_sgs
属性
base | (BSGS 的基础) |
|---|---|
generators | (BSGS 的生成器) |
rank | (张量的阶) |
classmethod direct_product(*args)
返回一个 TensorSymmetry 对象,它是完全(反)对称索引排列组的直积。
注释
一些 (*args) 的示例:(1) 矢量,相当于 TensorSymmetry.fully_symmetric(1) (2) 有 2 个对称索引的张量,相当于 .fully_symmetric(2) (-2) 有 2 个反对称索引的张量,相当于 .fully_symmetric(-2) (2, -2) 第一个 2 个索引交换,最后 2 个反交换的张量 (1, 1, 1) 有 3 个索引且没有任何对称性的张量
classmethod fully_symmetric(rank)
返回一个完全对称(如果 pyrank``<0) TensorSymmetry object for ``abs(rank) 索引则反对称)的张量。
classmethod no_symmetry(rank)
返回一个 rank 没有对称性的张量对称性对象。
classmethod riemann()
返回黎曼张量的单调对称性。
sympy.tensor.tensor.tensorsymmetry(*args)
返回一个 TensorSymmetry 对象。此方法已弃用,请使用 TensorSymmetry.direct_product() 或 .riemann() 替代。
解释
可以使用 BSGS 表示任何单项槽对称性群的张量。
args 可以是 BSGS args[0] 的基础 args[1] 的 sgs
通常张量位于(直积的)对称群的表示中;args 可以是表示 Young 表的形状列表的列表。
注释
例如:[[1]] 向量 [[1]*n] 秩为 n 的对称张量 [[n]] 秩为 n 的反对称张量 [[2, 2]] 黎曼张量的单项对称性 [[1],[1]] 向量*向量 [[2],[1],[1](反对称张量)向量向量
注意,对于形状 [2, 2],我们只与黎曼张量的单项对称性相关联;这是符号滥用,因为形状 [2, 2] 通常对应于由单项对称性和循环对称性特征化的不可约表示。
class sympy.tensor.tensor.TensorType(*args, **kwargs)
张量类型类。已弃用,请改用 tensor_heads()。
参数:
index_types:张量索引的 TensorIndexType 列表
symmetry:张量的 TensorSymmetry
属性
index_types | |
|---|---|
symmetry | |
types | (无重复的 TensorIndexType 列表) |
class sympy.tensor.tensor._TensorManager
类用于管理张量属性。
注意
张量属于张量交换群;每个群有一个标签 comm;有预定义的标签:
0 张量与任何其他张量交换
1 张量彼此反交换
2 张量不交换,与 comm=0 的张量分开
可以使用 set_comm 定义其他组;这些组中的张量与 comm=0 的张量交换;默认情况下,它们不与任何其他组交换。
clear()
清除 TensorManager。
comm_i2symbol(i)
返回与交换群编号对应的符号。
comm_symbols2i(i)
获取与 i 对应的交换群编号。
i 可以是符号、数字或字符串。
如果 i 还没有定义其交换群编号,则设置为其交换群编号。
get_comm(i, j)
返回交换群编号 i, j 的交换参数
见 _TensorManager.set_comm
set_comm(i, j, c)
设置交换群 i, j 的交换参数 c。
参数:
i, j:表示交换群的符号
c:群交换编号
注意
i, j 可以是符号、字符串或数字,除了 0, 1 和 2 分别保留给交换、反交换张量和与任何其他组不交换的张量。对于其余情况,请使用此方法设置交换规则;默认情况下 c=None。
交换群编号 c 分配给与交换群符号对应的群;可以为
0 交换
1 反交换
None 无交换属性
示例
G 和 GH 与自己不交换,彼此之间交换;A 是交换的。
>>> from sympy.tensor.tensor import TensorIndexType, tensor_indices, TensorHead, TensorManager, TensorSymmetry
>>> Lorentz = TensorIndexType('Lorentz')
>>> i0,i1,i2,i3,i4 = tensor_indices('i0:5', Lorentz)
>>> A = TensorHead('A', [Lorentz])
>>> G = TensorHead('G', [Lorentz], TensorSymmetry.no_symmetry(1), 'Gcomm')
>>> GH = TensorHead('GH', [Lorentz], TensorSymmetry.no_symmetry(1), 'GHcomm')
>>> TensorManager.set_comm('Gcomm', 'GHcomm', 0)
>>> (GH(i1)*G(i0)).canon_bp()
G(i0)*GH(i1)
>>> (G(i1)*G(i0)).canon_bp()
G(i1)*G(i0)
>>> (G(i1)*A(i0)).canon_bp()
A(i0)*G(i1)
set_comms(*args)
设置符号 i, j 的交换群编号 c。
参数:
args:(i, j, c) 的序列
张量运算符
class sympy.tensor.toperators.PartialDerivative(expr, *variables)
张量表达式的偏导数。
示例
>>> from sympy.tensor.tensor import TensorIndexType, TensorHead
>>> from sympy.tensor.toperators import PartialDerivative
>>> from sympy import symbols
>>> L = TensorIndexType("L")
>>> A = TensorHead("A", [L])
>>> B = TensorHead("B", [L])
>>> i, j, k = symbols("i j k")
>>> expr = PartialDerivative(A(i), A(j))
>>> expr
PartialDerivative(A(i), A(j))
PartialDerivative 对象的行为类似张量表达式:
>>> expr.get_indices()
[i, -j]
注意求导变量的价度与打印的相反:A(j) 打印为协变,但导数的指标实际上是逆变的,即 -j。
指标可以被缩并:
>>> expr = PartialDerivative(A(i), A(i))
>>> expr
PartialDerivative(A(L_0), A(L_0))
>>> expr.get_indices()
[L_0, -L_0]
方法 .get_indices() 总是返回所有指标(即使是缩并的)。如果只需要未缩并的指标,请调用 .get_free_indices():
>>> expr.get_free_indices()
[]
嵌套偏导数被展开:
>>> expr = PartialDerivative(PartialDerivative(A(i), A(j)), A(k))
>>> expr
PartialDerivative(A(i), A(j), A(k))
>>> expr.get_indices()
[i, -j, -k]
用数组值替换导数:
>>> from sympy.abc import x, y
>>> from sympy import sin, log
>>> compA = [sin(x), log(x)*y**3]
>>> compB = [x, y]
>>> expr = PartialDerivative(A(i), B(j))
>>> expr.replace_with_arrays({A(i): compA, B(i): compB})
[[cos(x), 0], [y**3/x, 3*y**2*log(x)]]
返回的数组由 ((i, -j)) 索引。
注意其他 SymPy 模块在导数结果中将求导变量的指标放在被导变量的指标之前。例如:
>>> expr.get_free_indices()
[i, -j]
>>> from sympy import Matrix, Array
>>> Matrix(compA).diff(Matrix(compB)).reshape(2, 2)
[[cos(x), y**3/x], [0, 3*y**2*log(x)]]
>>> Array(compA).diff(Array(compB))
[[cos(x), y**3/x], [0, 3*y**2*log(x)]]
这些是 PartialDerivative 的转置,因为矩阵和数组模块在导数结果中将指标 (-j) 放在 (i) 前面。用指标顺序 (-j, i) 读取的数组确实是用指标顺序 (i, -j) 读取的同一数组的转置。通过指定 .replace_with_arrays 的指标顺序,可以得到兼容的表达式:
>>> expr.replace_with_arrays({A(i): compA, B(i): compB}, [-j, i])
[[cos(x), y**3/x], [0, 3*y**2*log(x)]]
矢量
矢量模块提供了基本的矢量数学和相对于 3D 笛卡尔坐标系的微分计算工具。此文档概述了提供的所有功能及相关 API。
矢量指南
-
介绍
-
基本实现细节
-
关于坐标系的更多信息
-
标量和矢量场功能
-
使用的一般示例
-
矢量积分的应用
-
矢量 API
-
sympy.vector 中的基本类(文档字符串)
-
定向器类(文档字符串)
-
sympy.vector 中的基本函数(文档字符串)
-
矢量的参考资料
[Dyadics]
[DyadicProducts]
en.wikipedia.org/wiki/Dyadic_product
[DelOperator]
引言
本页提供了对 sympy.vector 模块功能的简要概述。
矢量和标量
在矢量数学中,我们处理两种类型的量:标量和矢量。
标量是仅具有大小而没有方向的实体。标量量的例子包括质量、电荷、温度、距离等。
另一方面,矢量是由大小和方向特征的实体。矢量量的例子包括位移、速度、磁场等。
标量可以仅用一个数字表示,例如 300 K 的温度。另一方面,加速度等矢量量通常用矢量表示。给定一个矢量 (\mathbf{V}),相应量的大小可以计算为矢量本身的大小 (\Vert \mathbf{V} \Vert),而方向则由原矢量方向上的单位矢量指定,(\mathbf{\hat{V}} = \frac{\mathbf{V}}{\Vert \mathbf{V} \Vert})。
例如,考虑位移为 ((3\mathbf{\hat{i}} + 4\mathbf{\hat{j}} + 5\mathbf{\hat{k}})) 米的情况,其中,按照标准惯例,(\mathbf{\hat{i}})、(\mathbf{\hat{j}}) 和 (\mathbf{\hat{k}}) 分别表示沿 (\mathbf{X})、(\mathbf{Y}) 和 (\mathbf{Z}) 轴的单位向量。因此,可以得出行程为 (\Vert 3\mathbf{\hat{i}} + 4\mathbf{\hat{j}} + 5\mathbf{\hat{k}} \Vert) 米 = (5\sqrt{2}) 米。行进方向由单位向量 (\frac{3}{5\sqrt{2}}\mathbf{\hat{i}} + \frac{4}{5\sqrt{2}}\mathbf{\hat{j}} + \frac{5}{5\sqrt{2}}\mathbf{\hat{k}}) 给出。
坐标系
坐标系是用来定义 n 维空间中方向和位置概念的抽象数学实体。本模块处理的是三维空间,传统的 (X)、(Y) 和 (Z) 轴分别相对于每个坐标系定义。
每个坐标系还有一个称为“原点”的特殊参考点。这一点在引用三维空间中的位置或计算相对于系统的预定义点的坐标时使用。
这是一个相当知名的概念:在空间中没有绝对的位置或方向的概念。任何给定的坐标系都定义了一个独特的“视角”,用来量化位置和方向。因此,即使我们假设所有系统都使用相同的测量单位,矢量和标量量的表达也会根据某个观察者使用的坐标系而有所不同。
考虑空间中的两点(P)和(Q)。假设单位在整个过程中是通用的,这两点之间的距离不变,无论在哪个坐标系中进行测量。然而,每个点的三维坐标以及任一点相对于另一点的位置矢量并不会保持不变。事实上,除非它们是在考虑某一位置和测量者的方向(本质上是坐标系)的情况下进行测量,否则这两个量根本就没有意义。
因此,很明显,坐标系的方向和位置(原点)定义了不同量如何相对于它来表达。这两个属性都不能在绝对尺度上进行测量,而是相对于另一个坐标系来测量。一个系统相对于另一个系统的方向是使用旋转矩阵来测量的,而相对位置可以通过一个系统原点到另一个系统原点的位置矢量来量化。
场
场是可以作为位置的函数在空间的任何地方指定的矢量或标量数量(注意,通常场也可能依赖于时间和其他自定义变量)。由于我们在本模块中只处理三维空间,因此场被定义为与坐标系中位置对应的(x)、(y)和(z)坐标的函数。在这里,(x)、(y)和(z)充当定义一般点位置的标量变量。
例如,三维空间中的温度(温度场)可以写成(T(x, y, z)) – 位置的标量函数。在电磁学中,标量场的一个例子是电势。
类似地,可以将矢量场定义为空间中任意点位置((x, y, z))的矢量函数。
例如,地球上的每一个点都可以被认为处于地球的重力场中。我们可以通过每个空间点处的加速度大小和方向(即单位质量的力)(\vec g(x, y, z)) 来指定该场。
举例来说,考虑一个三维空间中形式为(2{x}^{2}y)的电势场。相应的保守电场可以计算为电势函数的梯度,并表示为(4xy\mathbf{\hat{i}} + 2{x}^{2}\mathbf{\hat{j}})。该电场的大小反过来可以表示为形如(\sqrt{4{x}^{4} + 16{x}^{2}{y}^{2}})的标量场。
基本实现细节
坐标系和向量
目前,sympy.vector 能够处理笛卡尔(也称为矩形)、球面和其他曲线坐标系。
可以在 sympy.vector 中初始化 3D 笛卡尔坐标系。
>>> from sympy.vector import CoordSys3D
>>> N = CoordSys3D('N')
构造函数的字符串参数表示分配给系统的名称,并且主要用于打印目的。
一旦定义了坐标系(本质上是 CoordSys3D 实例),我们可以访问标准单位向量(即 (\mathbf{\hat{i}})、(\mathbf{\hat{j}}) 和 (\mathbf{\hat{k}}) 向量)和坐标变量/基标量(即 (\mathbf{x})、(\mathbf{y}) 和 (\mathbf{z}) 变量)。关于坐标变量我们将在后面的章节中详细讨论。
可以使用 i、j 和 k 属性分别访问 (X)、(Y) 和 (Z) 轴的基向量。
>>> N.i
N.i
>>> type(N.i)
<class 'sympy.vector.vector.BaseVector'>
如上所示,基向量都是名为 BaseVector 的类的实例。
当 BaseVector 乘以标量(实质上是任何 SymPy Expr)时,我们得到 VectorMul - 基向量与标量的乘积。
>>> 3*N.i
3*N.i
>>> type(3*N.i)
<class 'sympy.vector.vector.VectorMul'>
VectorMul 和 BaseVectors 的加法形成 VectorAdd - 当然,除了特殊情况。
>>> v = 2*N.i + N.j
>>> type(v)
<class 'sympy.vector.vector.VectorAdd'>
>>> v - N.j
2*N.i
>>> type(v - N.j)
<class 'sympy.vector.vector.VectorMul'>
零向量怎么办?可以使用分配给 Vector 类的 zero 属性访问。由于零向量的概念在考虑的坐标系中保持不变,我们在需要这种量时使用 Vector.zero。
>>> from sympy.vector import Vector
>>> Vector.zero
0
>>> type(Vector.zero)
<class 'sympy.vector.vector.VectorZero'>
>>> N.i + Vector.zero
N.i
>>> Vector.zero == 2*Vector.zero
True
所有上述类 - BaseVector、VectorMul、VectorAdd 和 VectorZero 都是 Vector 的子类。
您永远不应该实例化 Vector 的任何子类的对象。使用分配给 CoordSys3D 实例的 BaseVector 实例和(如果需要)Vector.zero 作为基础,可以使用基本数学运算符 +、-、* 和 / 构建任何类型的向量表达式。
>>> v = N.i - 2*N.j
>>> v/3
1/3*N.i + (-2/3)*N.j
>>> v + N.k
N.i + (-2)*N.j + N.k
>>> Vector.zero/2
0
>>> (v/3)*4
4/3*N.i + (-8/3)*N.j
除了基本的数学运算外,还可以在 Vector 上执行 dot 和 cross 的向量运算。
>>> v1 = 2*N.i + 3*N.j - N.k
>>> v2 = N.i - 4*N.j + N.k
>>> v1.dot(v2)
-11
>>> v1.cross(v2)
(-1)*N.i + (-3)*N.j + (-11)*N.k
>>> v2.cross(v1)
N.i + 3*N.j + 11*N.k
dot 和 cross 方法的 & 和 ^ 操作符已重载。
>>> v1 & v2
-11
>>> v1 ^ v2
(-1)*N.i + (-3)*N.j + (-11)*N.k
然而,这不是执行这些操作的推荐方式。使用原始方法使代码更清晰,更易于理解。
除了这些操作外,在 sympy.vector 中还可以计算 Vector 实例的外积。稍后将详细介绍。
SymPy 向量的操作
SymPy 操作 simplify、trigsimp、diff 和 factor 适用于 Vector 对象,使用标准的 SymPy API。
本质上,这些方法是在提供的向量表达式中存在的测量数(基向量的系数)上操作。
>>> from sympy.abc import a, b, c
>>> from sympy import sin, cos, trigsimp, diff
>>> v = (a*b + a*c + b**2 + b*c)*N.i + N.j
>>> v.factor()
((a + b)*(b + c))*N.i + N.j
>>> v = (sin(a)**2 + cos(a)**2)*N.i - (2*cos(b)**2 - 1)*N.k
>>> trigsimp(v)
N.i + (-cos(2*b))*N.k
>>> v.simplify()
N.i + (-cos(2*b))*N.k
>>> diff(v, b)
(4*sin(b)*cos(b))*N.k
>>> from sympy import Derivative
>>> Derivative(v, b).doit()
(4*sin(b)*cos(b))*N.k
Integral也与Vector实例一起工作,类似于Derivative。
>>> from sympy import Integral
>>> v1 = a*N.i + sin(a)*N.j - N.k
>>> Integral(v1, a)
(Integral(a, a))*N.i + (Integral(sin(a), a))*N.j + (Integral(-1, a))*N.k
>>> Integral(v1, a).doit()
a**2/2*N.i + (-cos(a))*N.j + (-a)*N.k
点
如前所述,每个坐标系对应于一个唯一的原点。一般来说,点已经在sympy.vector中以Point类的形式实现。
要访问系统的原点,请使用CoordSys3D类的origin属性。
>>> from sympy.vector import CoordSys3D
>>> N = CoordSys3D('N')
>>> N.origin
N.origin
>>> type(N.origin)
<class 'sympy.vector.point.Point'>
您可以使用Point的locate_new方法在空间中实例化新点。参数包括新Point的名称(字符串)及其相对于“父”Point的位置向量。
>>> from sympy.abc import a, b, c
>>> P = N.origin.locate_new('P', a*N.i + b*N.j + c*N.k)
>>> Q = P.locate_new('Q', -b*N.j)
像Vector一样,用户永远不必显式实例化Point对象。这是因为可以通过使用CoordSys3D的origin作为参考来指向空间中的任何位置(尽管是相对位置),然后在其上使用locate_new和后续的Point实例。
可以使用position_wrt方法计算一个Point相对于另一个Point的位置向量。
>>> P.position_wrt(Q)
b*N.j
>>> Q.position_wrt(N.origin)
a*N.i + c*N.k
此外,可以通过express_coordinates方法获取相对于CoordSys3D的Point的(X)、(Y)和(Z)坐标,以元组的形式表示。
>>> Q.express_coordinates(N)
(a, 0, c)
二阶张量
二阶张量,或者说二阶张量,是由向量对并列形成的。因此,向量的外积导致二阶张量的形成。在sympy.vector中,已经用Dyadic类实现了二阶张量。
再次强调,您永远不需要实例化Dyadic对象。可以使用Vector的outer方法计算向量的外积。|运算符已经为outer重载。
>>> from sympy.vector import CoordSys3D
>>> N = CoordSys3D('N')
>>> N.i.outer(N.j)
(N.i|N.j)
>>> N.i|N.j
(N.i|N.j)
类似于Vector,Dyadic也有像BaseDyadic、DyadicMul、DyadicAdd这样的后续子类。与Vector类似,可以从Dyadic.zero获取零二阶张量。
所有基本数学运算也适用于Dyadic。
>>> dyad = N.i.outer(N.k)
>>> dyad*3
3*(N.i|N.k)
>>> dyad - dyad
0
>>> dyad + 2*(N.j|N.i)
(N.i|N.k) + 2*(N.j|N.i)
dot和cross在Dyadic实例之间以及Dyadic与Vector之间(反之亦然)也有效,如各自的数学定义。与Vector类似,&和^已经为dot和cross重载。
>>> d = N.i.outer(N.j)
>>> d.dot(N.j|N.j)
(N.i|N.j)
>>> d.dot(N.i)
0
>>> d.dot(N.j)
N.i
>>> N.i.dot(d)
N.j
>>> N.k ^ d
(N.j|N.j)