Python de 対称群 3

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

#import

```python
%matplotlib inline
import numpy as np
import math
from sympy import *
from sympy.combinatorics import *

init_printing(pretty_print=False)
```

#関数
[Python de 対称群 2 - Qiita](https://qiita.com/Krypf/items/5929bfe1980e7f4dcf0a) または，
[Python de 対称群 2 - Krypf’s Diary](https://krypf.hatenablog.com/entry/2020/12/17/143151) 参照．

```python
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 = 4
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))]

def list_multi(list_of_permutations):
    return [list(list_of_permutations[i]) for i in range(len(list_of_permutations))]

def sgn(x):
    return x.signature()
```

#類なしソートなしで普通に
共役類に分けずなんのソートもなしで表示してみる．

##表示

```python
Symn_perm = SymmetricGroup(n)._elements
plusone_multi(Symn_perm)
'''
[(4), (1 2 3 4), (1 3)(2 4), (1 4), (2 3 4), (1 2 4 3), (4)(1 3 2), (1 4 2 3), (2 4 3), (4)(1 2), (1 3 4), (1 4 3 2), 
(3 4), (4)(1 2 3), (1 3 2 4), (1 4 3), (4)(2 3), (1 2 4), (1 3 4 2), (1 4)(2 3), (2 4), (1 2)(3 4), (4)(1 3), (1 4 2)]
'''
```

```python
Symn_list = list_multi(plusone_multi(Symn_perm))
Symn_list, len(Symn_list)
'''
([[0, 1, 2, 3, 4], [0, 2, 3, 4, 1], [0, 3, 4, 1, 2], [0, 4, 2, 3, 1], [0, 1, 3, 4, 2], [0, 2, 4, 1, 3], [0, 3, 1, 2, 4], [0, 4, 3, 1, 2], [0, 1, 4, 2, 3], [0, 2, 1, 3, 4], [0, 3, 2, 4, 1], [0, 4, 1, 2, 3], 
[0, 1, 2, 4, 3], [0, 2, 3, 1, 4], [0, 3, 4, 2, 1], [0, 4, 2, 1, 3], [0, 1, 3, 2, 4], [0, 2, 4, 3, 1], [0, 3, 1, 4, 2], [0, 4, 3, 2, 1], [0, 1, 4, 3, 2], [0, 2, 1, 4, 3], [0, 3, 2, 1, 4], [0, 4, 1, 3, 2]], 24)
'''
```

##各元を判別する関数


```python
def discriminate_perm(x):
    for i in range(len(Symn_perm)): 
        if x == Symn_perm[i] or x == plusone(Symn_perm[i]):
            return 'g' + str(i)
        elif x == Symn_list[i] or x == list(plusone(Symn_list[i])):
            return 'g' + str(i)
        
[ [discriminate_perm(Symn_perm[i]), plusone(Symn_perm[i]), sgn(Symn_perm[i] ) ] 
 for i in range(len(Symn_perm))]

'''
[['g0', Permutation(4), 1],
 ['g1', Permutation(1, 2, 3, 4), -1],
 ['g2', Permutation(1, 3)(2, 4), 1],
 ['g3', Permutation(1, 4), -1],
 ['g4', Permutation(2, 3, 4), 1],
 ['g5', Permutation(1, 2, 4, 3), -1],
 ['g6', Permutation(4)(1, 3, 2), 1],
 ['g7', Permutation(1, 4, 2, 3), -1],
 ['g8', Permutation(2, 4, 3), 1],
 ['g9', Permutation(4)(1, 2), -1],
 ['g10', Permutation(1, 3, 4), 1],
 ['g11', Permutation(1, 4, 3, 2), -1],
 ['g12', Permutation(3, 4), -1],
 ['g13', Permutation(4)(1, 2, 3), 1],
 ['g14', Permutation(1, 3, 2, 4), -1],
 ['g15', Permutation(1, 4, 3), 1],
 ['g16', Permutation(4)(2, 3), -1],
 ['g17', Permutation(1, 2, 4), 1],
 ['g18', Permutation(1, 3, 4, 2), -1],
 ['g19', Permutation(1, 4)(2, 3), 1],
 ['g20', Permutation(2, 4), -1],
 ['g21', Permutation(1, 2)(3, 4), 1],
 ['g22', Permutation(4)(1, 3), -1],
 ['g23', Permutation(1, 4, 2), 1]]
'''
```

#共役類に分ける

##表示

もし行ごとに print すれば何をやっているのかわかる．

```python
conjc = SymmetricGroup(n).conjugacy_classes()
conjc_list = [list(conjc[i]) for i in range(len(conjc))] #not for i in range(n)!!!


Symn_conj = [ conjc_list[i][j]  for i in range(len(conjc)) for j in range(len(conjc_list[i]))]
len(Symn_conj), plusone_multi(Symn_conj)

'''
(24, [(4), (1 4 2 3), (1 2 3 4), (1 3 2 4), (1 2 4 3), (1 4 3 2), (1 3 4 2), (1 4)(2 3), (1 3)(2 4), (1 2)(3 4), (2 4), (4)(1 3), (3 4), (4)(1 2), (1 4), (4)(2 3), (1 4 2), (1 2 4), (1 3 4), (1 4 3), (2 4 3), (2 3 4), (4)(1 3 2), (4)(1 2 3)])
'''
結果長いけど改行するのやめちゃった

```

※12/17 と 12/20 で共役類内の順番が変わっていたのだが，12/20 の分を表示している．ひょっとするとアップデートされて内部でプログラムが書き換わったのではないか（しかし12/20時点で sympy.combinatorics の最終更新は12/12．原因不明）．


```python
[ list_multi( plusone_multi (conjc_list[i] )) for i in range(n)]
'''
[[[0, 1, 2, 3, 4]], [[0, 4, 3, 1, 2], [0, 2, 3, 4, 1], [0, 3, 4, 2, 1], [0, 2, 4, 1, 3], [0, 4, 1, 2, 3], [0, 3, 1, 4, 2]], [[0, 4, 3, 2, 1], [0, 3, 4, 1, 2], [0, 2, 1, 4, 3]], [[0, 1, 4, 3, 2], [0, 3, 2, 1, 4], [0, 1, 2, 4, 3], [0, 2, 1, 3, 4], [0, 4, 2, 3, 1], [0, 1, 3, 2, 4]]]
'''
```


##各元を判別する関数


```python
def discriminate_conj(x):
    for i in range(len(Symn_conj)): 
        if x == Symn_conj[i] or x == plusone(Symn_conj[i]):
            return 'g' + str(i)
        elif x == Symn_conj[i] or x == list(plusone(Symn_conj[i])):
            return 'g' + str(i)
        
[ [discriminate_conj(Symn_conj[i]), plusone(Symn_conj[i]), sgn(Symn_conj[i] ) ] 
 for i in range(len(Symn_conj))]

'''
[['g0', Permutation(4), 1],
 ['g1', Permutation(1, 4, 2, 3), -1],
 ['g2', Permutation(1, 2, 3, 4), -1],
 ['g3', Permutation(1, 3, 2, 4), -1],
 ['g4', Permutation(1, 2, 4, 3), -1],
 ['g5', Permutation(1, 4, 3, 2), -1],
 ['g6', Permutation(1, 3, 4, 2), -1],
 ['g7', Permutation(1, 4)(2, 3), 1],
 ['g8', Permutation(1, 3)(2, 4), 1],
 ['g9', Permutation(1, 2)(3, 4), 1],
 ['g10', Permutation(2, 4), -1],
 ['g11', Permutation(4)(1, 3), -1],
 ['g12', Permutation(3, 4), -1],
 ['g13', Permutation(4)(1, 2), -1],
 ['g14', Permutation(1, 4), -1],
 ['g15', Permutation(4)(2, 3), -1],
 ['g16', Permutation(1, 4, 2), 1],
 ['g17', Permutation(1, 2, 4), 1],
 ['g18', Permutation(1, 3, 4), 1],
 ['g19', Permutation(1, 4, 3), 1],
 ['g20', Permutation(2, 4, 3), 1],
 ['g21', Permutation(2, 3, 4), 1],
 ['g22', Permutation(4)(1, 3, 2), 1],
 ['g23', Permutation(4)(1, 2, 3), 1]]
'''
```

#更に行列式でソートして交代群が現れるようにする

もうクラインの4元群は現れているが交代群が見にくいのでここからさらに行列式でソートする．
##まずはソート

```python

def sortsgn(perms):
    sortedlist = [perms[k] for k in range(len(perms))]
    for i in range(len(perms)):
        for j in range(i, 0 ,-1):
            if sgn(sortedlist[j - 1]) < sgn(sortedlist[j]):
                sortedlist[j - 1], sortedlist[j] = sortedlist[j], sortedlist[j - 1]      
    return sortedlist

Symn_conj_sort = sortsgn(Symn_conj)
[ [discriminate_conj(Symn_conj_sort[i]), plusone(Symn_conj_sort[i]), sgn(Symn_conj_sort[i] ) ]
 for i in range(len(Symn_conj_sort))]
'''

[['g0', Permutation(4), 1],
 ['g7', Permutation(1, 4)(2, 3), 1],
 ['g8', Permutation(1, 3)(2, 4), 1],
 ['g9', Permutation(1, 2)(3, 4), 1],
 ['g16', Permutation(1, 4, 2), 1],
 ['g17', Permutation(1, 2, 4), 1],
 ['g18', Permutation(1, 3, 4), 1],
 ['g19', Permutation(1, 4, 3), 1],
 ['g20', Permutation(2, 4, 3), 1],
 ['g21', Permutation(2, 3, 4), 1],
 ['g22', Permutation(4)(1, 3, 2), 1],
 ['g23', Permutation(4)(1, 2, 3), 1],
 ['g1', Permutation(1, 4, 2, 3), -1],
 ['g2', Permutation(1, 2, 3, 4), -1],
 ['g3', Permutation(1, 3, 2, 4), -1],
 ['g4', Permutation(1, 2, 4, 3), -1],
 ['g5', Permutation(1, 4, 3, 2), -1],
 ['g6', Permutation(1, 3, 4, 2), -1],
 ['g10', Permutation(2, 4), -1],
 ['g11', Permutation(4)(1, 3), -1],
 ['g12', Permutation(3, 4), -1],
 ['g13', Permutation(4)(1, 2), -1],
 ['g14', Permutation(1, 4), -1],
 ['g15', Permutation(4)(2, 3), -1]]
'''
```


##この順番で判別する関数

判別する関数をソート後の順番に書き直す．

```python
def discriminate_conj_sort(x):
    for i in range(len(Symn_conj_sort)): 
        if x == Symn_conj_sort[i] or x == plusone(Symn_conj_sort[i]):
            return 'g' + str(i)
        elif x == Symn_conj_sort[i] or x == list(plusone(Symn_conj_sort[i])):
            return 'g' + str(i)
        
[ [discriminate_conj_sort(Symn_conj_sort[i]), plusone(Symn_conj_sort[i]), sgn(Symn_conj_sort[i] ) ] 
 for i in range(len(Symn_conj_sort))]

'''

[['g0', Permutation(4), 1],
 ['g1', Permutation(1, 4)(2, 3), 1],
 ['g2', Permutation(1, 3)(2, 4), 1],
 ['g3', Permutation(1, 2)(3, 4), 1],
 ['g4', Permutation(1, 4, 2), 1],
 ['g5', Permutation(1, 2, 4), 1],
 ['g6', Permutation(1, 3, 4), 1],
 ['g7', Permutation(1, 4, 3), 1],
 ['g8', Permutation(2, 4, 3), 1],
 ['g9', Permutation(2, 3, 4), 1],
 ['g10', Permutation(4)(1, 3, 2), 1],
 ['g11', Permutation(4)(1, 2, 3), 1],
 ['g12', Permutation(1, 4, 2, 3), -1],
 ['g13', Permutation(1, 2, 3, 4), -1],
 ['g14', Permutation(1, 3, 2, 4), -1],
 ['g15', Permutation(1, 2, 4, 3), -1],
 ['g16', Permutation(1, 4, 3, 2), -1],
 ['g17', Permutation(1, 3, 4, 2), -1],
 ['g18', Permutation(2, 4), -1],
 ['g19', Permutation(4)(1, 3), -1],
 ['g20', Permutation(3, 4), -1],
 ['g21', Permutation(4)(1, 2), -1],
 ['g22', Permutation(1, 4), -1],
 ['g23', Permutation(4)(2, 3), -1]]
'''

```


これで工夫すれば群表も書けるよ（5次で1 min，6次で3.3 h）．終わり．



#あとがき

対称群をこれほどシンプルに扱える言語は Python だけなのではないか．「対称群　プログラミング言語」などと調べても Wolfram や Egison (日本人が作った言語でアインシュタインの縮約記法などもつかえるらしい) しかヒットしない．Wolfram は例示が少なく（しかも有料版は安くない），Egisonに至っては新しすぎて調べても演算などが可能かどうか不明である．

対称群を Python で計算している途中，コマンド一つで共役類を呼び出したり，自由自在に型を変換したり演算を行ったり，感動すら覚えた．

数学ガール「ガロア理論」にも登場する対称群，今一度 Python を使って理解したら，懐かしい気持ちになるだろうか，それとも開けた世界に一歩踏み出したいような気持ちになるだろうか．

任意の有限群は対称群の部分群に同型である，というケーリーの定理からもわかるように，対称群の峰々には，豊かで美しく，青くて深い森が広がっている．この記事が，山頂からの絶景を眺める一助となれば，幸いである．