Krypf’s Diary

暇なときに書く

Python de 対称群 2

plusone() 関数の改良

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

1 前回の関数

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

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]に掛けたものを書いてみる.

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 にした関数と複数の置換のリストを扱えるよう改良した関数.

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)]
'''

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

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 で統一的に対称群の元を扱えるようになった.このほうがどう考えてもシンプルである.

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次対称群の要素を表示する.

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]に置換を掛けた結果)に直してみる.

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 で出来ることに気づいたので計算時間を測定して比較してみる.

まずは上の.

%%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.

%%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 する必要もなさそうだ.