【Python】SymPy の symbol の基本と応用 

<!-- SymPy の symbol の基本と応用 -->
<!-- ee Basics and applications of SymPy symbols -->
# 0. はじめに
<!-- ee # 0. Introduction -->

本稿では SymPy における文字の定義を整理します．
<!-- ee This article aims to summarize how to define symbols using SymPy. -->
SymPy とは代数計算を目的とした Python の ライブラリです．[^1]
<!-- ee SymPy is a Python library for symbolic or algebraic calculations. [^1] -->
SymPy がインストールされていることは前提します．
<!-- ee We presuppose that SymPy is installed. -->

```python
import sympy as sy
print(sy.__version__)
# 1.11
```

# 1. abc

まずプログラムの頭でできることは abc モジュールから用意された文字を import することです．[^2]


```python
from sympy.abc import x,y,z
print(x,y,z)
print(type(x))
# x y z
# <class 'sympy.core.symbol.Symbol'>
```

abc モジュールのソースコードを見ると使用可能な文字の一覧がわかります．
```python
a, b, c, d, e, f, g, h, i, j = symbols('a, b, c, d, e, f, g, h, i, j')
k, l, m, n, o, p, q, r, s, t = symbols('k, l, m, n, o, p, q, r, s, t')
u, v, w, x, y, z = symbols('u, v, w, x, y, z')

A, B, C, D, E, F, G, H, I, J = symbols('A, B, C, D, E, F, G, H, I, J')
K, L, M, N, O, P, Q, R, S, T = symbols('K, L, M, N, O, P, Q, R, S, T')
U, V, W, X, Y, Z = symbols('U, V, W, X, Y, Z')

alpha, beta, gamma, delta = symbols('alpha, beta, gamma, delta')
epsilon, zeta, eta, theta = symbols('epsilon, zeta, eta, theta')
iota, kappa, lamda, mu = symbols('iota, kappa, lamda, mu')
nu, xi, omicron, pi = symbols('nu, xi, omicron, pi')
rho, sigma, tau, upsilon = symbols('rho, sigma, tau, upsilon')
phi, chi, psi, omega = symbols('phi, chi, psi, omega')
```

lambda ではなく重複を避けて **lamda** とされていることに注意しましょう．
symbols 関数については後述することにしましょう．


# 2. Symbol

次に1つ文字を定義するやり方です．
それが `Symbol` です．

```python
from sympy import Symbol
# from sympy import *
x = Symbol("x")
xi = Symbol("xi")
print(x,xi)
# x xi
```

最も基本的な方法なので多くのサンプルコードで使用されています．

# 3. symbols

複数の文字を一度に定義するには `symbols` を使います．式の区切りには**スペースかコンマ**を入れます．出力はタプルです．

```python
from sympy import symbols
xyz = symbols("x y z")
print(xyz)
print(xyz == symbols("x, y, z"))
# (x, y, z)
# True
```

文字式で四則演算等が行なえます．

```python
x,y,z = xyz
print(x * y * z)
print(x + y + z)
# x*y*z
# x + y + z
```

# 4.【応用】整数添字付き記号を定義する

応用としてコロンを用いた添字記法を見てみましょう．ここでは整数添字，つまり
```math
x = (x_{\mu})_{\mu \in M},\quad M ⊆ \mathbb{Z}
```
を表現する方法を典型的なものとして例に取ります．

## 4.1 非負整数

$M ⊆ \mathbb{N} = \{0,1,2,3,\dots\}$ の時は `symbols` と `:` を使って以下のように文字式を生成することができます．

```python
from sympy import symbols
x = symbols("x0:"+str(3+1))
print(x)
# (x0, x1, x2, x3)
```

## 4.2 負数

最後に，$M \subsetneq \mathbb{N} $ の時つまり負の数を添え字にもつ場合を考えます．
Python 3.8.6 の時点で `"x_-1"` という表記が可能なのでこれを使います．
級数展開の部分和を以下のプログラムで計算できます．負冪は Laurent 展開の時に出てきます．

`Symbol` には（群や多元環としての）積の可換性を指定するオプションがあり今回は係数を冪の前に表示するために非可換にしておきます．係数同士も非可換になります．

非負添字の `Coefplus` と負添字の `Coefminus` をタプルとして結合します．

そして係数と冪の積和を取り多項式（正確には有理式）を生成します．

```python
N0 = 4
def Coefs(x: str,N: int = N0,com: bool =False):
    Coefplus = symbols(x+'0:'+str(N+1),commutative=com)
    Coefminus = symbols(x+'_-1:'+str(N+1),commutative=com)[::-1]# [::-1] means reversed
    return Coefplus + Coefminus

def _polynomial(a_str,x,N=N0):
    return sum([Coefs(a_str)[n] * x**n for n in range(-N,N+1)])

x = Symbol("x",commutative = False)
print(x + Coefs("a")[0] ==  Coefs("a")[0] + x)
print(x * Coefs("a")[0] ==  Coefs("a")[0] * x)
print(_polynomial("a",x))
# True
# False
# a0 + a1*x + a2*x**2 + a3*x**3 + a4*x**4 + a_-1*x**(-1) + a_-2*x**(-2) + a_-3*x**(-3) + a_-4*x**(-4)
```

最終行は以下のような $-4$ 次から $4$ 次までの出力です．

```math
a_{0} + a_{1} x + a_{2} x^{2} + a_{3} x^{3} + a_{4} x^{4} + a_{-1} x^{-1} + a_{-2} x^{-2} + a_{-3} x^{-3} + a_{-4} x^{-4}
```

## 4.3 可換性

積の可換性を変更すると出力，つまり積の順番も変わり得ます．可換演算について順番をうまく指定する方法は特にないのでその時は順序を無視して計算してよいです．

### 参考例

例えば．殆どの記法，特に Python では和は可換演算です．差はその逆演算だから和に書き換えられて，特に反可換です．
もし仮に $x - \alpha $ と表示させたい所 $- \alpha + x $ と出力されたとしても，そのまま計算すればよいです．

```python
from sympy.abc import x, alpha
print(x - alpha)
# -alpha + x
```

# 5. その他補足

# 5.1 アルファベット

コロンの文法で連続アルファベットの組を生成することができます．

```python
print(symbols('w:z'))
print(symbols('a:z'))
# (w, x, y, z)
# (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z)
```

# 5.2 var

`var` 関数でアルファベットを global 変数として定義することを宣言できます．

```python
from sympy import var

var('X, Y', commutative=False)
print(X * Y - Y * X)
# X*Y - Y*X
```

ソースコードに

```
    Create symbols and inject them into the global namespace.

    Explanation
    ===========

    This calls :func:`symbols` with the same arguments and puts the results
    into the *global* namespace. It's recommended not to use :func:`var` in
    library code, where :func:`symbols` has to be used::
```

とあります．VSCode 上では未定義として扱われ Warning が出ます．

# 5.3 display

本稿では簡単のため出力に `print` を用いましたが LaTeX 式のイタリック表記等ができてそれがしたい時は `display` 関数を使います．


# 6. おわりに

SymPy を使って記号計算を行うことができ，高い表現力をもちほとんど場合軽いです．
`sympy.abc, Symbol, symbols` を使いこなすことで複雑な計算処理を手計算なしに終わらせることができます．


## 参考

1. [SymPy.org](https://www.sympy.org/en/index.html)
2. [abc - SymPy 1.11 documentation](https://docs.sympy.org/latest/modules/abc.html)
3. [SymPy - Symbols](https://www.tutorialspoint.com/sympy/sympy_symbols.htm)

[^1]: [SymPy.org](https://www.sympy.org/en/index.html) "About"

[^2]: [abc - SymPy 1.11 documentation](https://docs.sympy.org/latest/modules/abc.html)