Python de 対称群 2

#plusone() 関数の改良

どうもどこかで見たはずの，前回の tuple でやる方式は，スタンダードではないらしいので，list に変えて，更に拡張・改良する．

##1 前回の関数
以前の関数．出力はコメントアウトで表現．名前は plusone_tuple() に変えた．

```python
import numpy as np
from sympy.combinatorics import *
from sympy import *
init_printing(pretty_print=False)

n = 3

def plusone_tuple(x):
    X = tuple([0])
    for i in range(n):
        a = tuple([tuple(x)[i] + 1])
        X = X + a
    return Permutation(tuple(X))

print(plusone_tuple(Permutation(0,1,2)))

#(1 2 3)
```


少し結合が面倒になったり，添字に注意が必要だったりするが，tuple でやるよりも list でやったほうがいい．
とりあえず，内包表記を使って3次対称群の元を[0, 1, 2]に掛けたものを書いてみる．

```python
A = PermutationGroup(SymmetricGroup(n)[0], SymmetricGroup(n)[1])._elements

print(A)
[list(A[i]) for i in range(len(A))]
'''
[Permutation(2), Permutation(0, 1, 2), Permutation(0, 2, 1), Permutation(1, 2), Permutation(2)(0, 1), Permutation(0, 2)]
[[0, 1, 2], [1, 2, 0], [2, 0, 1], [0, 2, 1], [1, 0, 2], [2, 1, 0]]
'''
```


##2 今回の関数
list にした関数と複数の置換のリストを扱えるよう改良した関数．

```python
from sympy.combinatorics import *
from sympy import *
init_printing(pretty_print=False)

def plusone(x):
    add_one = [(i > 0) * (list(x)[i - 1] + 1) for i in range(1 + len(list(x)))]
    #(i > 0) is a step function.
    return Permutation(add_one)

n = 3
def plusone_list(x):
    return plusone(x)

def plusone_multi(list_of_permutations):
    return [plusone(list_of_permutations[i]) for i in range(len(list_of_permutations))]

print(A) 
AA = plusone_multi(A)
AA

'''
[Permutation(2), Permutation(0, 1, 2), Permutation(0, 2, 1), Permutation(1, 2), Permutation(2)(0, 1), Permutation(0, 2)]
[(3), (1 2 3), (1 3 2), (2 3), (3)(1 2), (1 3)]
'''
```

ちなみに面倒くさいがこうしても同じである．

```python
generate_Sym3_perm = PermutationGroup(plusone(SymmetricGroup(n)[0]), plusone(SymmetricGroup(n)[1]))._elements
generate_Sym3_perm
'''
[(3), (1 2 3), (1 3 2), (2 3), (3)(1 2), (1 3)]
'''
```

以下の list_multi() が複数の置換のリストを list に変換する．これで tuple を介在させずに，list で統一的に対称群の元を扱えるようになった．このほうがどう考えてもシンプルである．

```python
def list_multi(list_of_permutations):
    return [list(list_of_permutations[i]) for i in range(len(list_of_permutations))]
print(list_multi(A))
print(list_multi(AA))
'''
[[0, 1, 2], [1, 2, 0], [2, 0, 1], [0, 2, 1], [1, 0, 2], [2, 1, 0]]
[[0, 1, 2, 3], [0, 2, 3, 1], [0, 3, 1, 2], [0, 1, 3, 2], [0, 2, 1, 3], [0, 3, 2, 1]]
'''
```

##3 3次対称群をシンプルに計算
3次対称群の要素を表示する．

```python
Sym3_perm = SymmetricGroup(n)._elements
Sym3_perm
'''
[(2), (0 1 2), (0 2 1), (1 2), (2)(0 1), (0 2)]
'''
```

1から始まる表示にして，リスト（[0,1,2,3]に置換を掛けた結果）に直してみる．

```python
Sym3_list = list_multi(plusone_multi(Sym3_perm))
Sym3_list
'''
[[0, 1, 2, 3], [0, 2, 3, 1], [0, 3, 1, 2], [0, 1, 3, 2], [0, 2, 1, 3], [0, 3, 2, 1]]
'''
```

得たかった結果がえられた．
置換とリストを相互に変換できなくなるので，0は残しておく．プログラムは，「3を固定して1, 2を入れ替え」を先に扱い，「2を固定して1, 3を入れ替え」= (1 3) を後に置いている．元の命名が課題になりそうだ．元の数が24になる4次対称群ならなおさらである．


#考察
np.array で出来ることに気づいたので計算時間を測定して比較してみる．

まずは上の．

```python
%%timeit
Sym3_list = list_multi(plusone_multi(Sym3_perm))
Sym3_list
#129 µs ± 6.92 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
#132 µs ± 13.1 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
#132 µs ± 13.1 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
```

numpy.

```python
%%timeit
Sym3_list_np = np.array(plusone_multi(Sym3_perm)).tolist()
Sym3_list_np
#139 µs ± 10.7 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
#135 µs ± 5.21 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
#143 µs ± 8.14 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
```

・1回目は list_multi → np.array

・2回目は np.array → list_multi

・3回目は並列に実行してみた．

誤差の範囲で同じ，と言えそうだ．
ただ，np.array を使って少し重くなるのか，list_multi() を用いたほうがこころなしか速く，安定しているように思う．ここでは，numpy を import する必要もなさそうだ．


前回記事：
[Python de 対称群 - Qiita](https://qiita.com/Krypf/items/615706d48f7475cb25a8)

次の記事：
[Python de 対称群 3 - Qiita](https://qiita.com/Krypf/items/1339ef03287408a05360)