リーチ格子にふれる
前回の記事では, -格子について調べる中で, 保型形式と格子との間の深いつながりを垣間見た. -格子のノルムに対する数え上げ公式が, 格子の偶ユニモジュラー性というありがたい性質を持つことから導かれるのだった.
今回の主役はリーチ格子 Leech latticeである.
前回とは少し順序を変えて, ウェイト12の保型形式のもつ性質に注目することから始めよう.
ウェイト12の保型形式
保型形式
以後, の保型形式のことを単に「保型形式」と呼ぶ.
に対して となることから, 奇数ウェイトの保型形式は存在しない.
偶数ウェイトなら存在できて, それを代表するのが なるウェイトを持つアイゼンシュタイン級数である.
アイゼンシュタイン級数
アイゼンシュタイン級数 は4以上の偶数に対して,
で定義される [Koblitz, 志賀]. 和は 以外の全ての整数の組に渡る.
正規化されたアイゼンシュタイン級数は, -展開の定数項が1になるように 倍したもので,
と定義される.
ここで, はゼータ関数. はベルヌーイ数で, 最初のいくつかは以下のような値をとる [Wiki1].
\begin{align}
\begin{array}{|c|cccccccc|} \hline
k & 0 & 1 & 2 & 4 & 6 & 8 & 10 & 12\\ \hline
B_k & 1 & -\frac{1}{2} & \frac{1}{6} & -\frac{1}{30} & \frac{1}{42} & -\frac{1}{30}
& \frac{5}{66} & -\frac{691}{2730} \\ \hline
\end{array}
\end{align}
なお, という関係がある.
以後「アイゼンシュタイン級数」と呼べばこの「正規化されたアイゼンシュタイン級数」のことを指すことにする.
最初のいくつかのアイゼンシュタイン級数は次のようになっている.
アイゼンシュタイン級数は保型形式であるが(略. [Koblitz, 志賀]), 単に「代表的な保型形式」であるだけではなく, 本質的に保型形式はアイゼンシュタイン級数だけから作られる.
次の定理が重要.
つまり, 保型形式は ふたつのアイゼンシュタイン級数 の多項式で尽きている.
この定理から次のことが分かる.
- ウェイト , また, が奇数のときは保型形式は存在しない.
- ウェイト のとき, 保型形式は定数である.
- ウェイト までは保型形式は定数倍の違いを除いて一つだけあって, それぞれ,
ウェイト に至って, モジュラー形式のなす空間は初めて2次元になる. その基底は, 次の2つ.
\begin{align}
E_4^3 &= (1+240q^2+2160q^4+\cdots)^3 = 1+720q^2+179280q^4+\cdots,\\
E_6^2 &= (1-504q^2-16632q^4+\cdots)^2 = 1-1008q^2+220752q^4+\cdots.
\end{align}
この2つを組み合わせてウェイト12の保型形式を作ろう. どれも という形をしているが, ここで注目するのは次の3つの場合.
この係数たちから, それぞれ異なった特徴をもった, 3つのウェイト12の保型形式が作られる.
12次のアイゼンシュタイン級数
まず, 12次のアイゼンシュタイン級数 もまたウェイト12の保型形式であるから, この2つの和で表される.
\begin{align}
E_{12} = 1+\frac{65520}{691}q^2+\cdots = \alpha E_4^3 + \beta E_6^2,
\end{align}
とすると,
\begin{align}
\alpha+\beta &= 1,\\
720\alpha -1008\beta &= \frac{65520}{691},
\end{align}
でなくてはならない. これを解いて,
\begin{align}
\alpha = \frac{441}{691},\ \beta = \frac{250}{691}
\end{align}
を得る. すなわち,
\begin{align}
E_{12} = \frac{441E_4^3 + 250E_6^2}{691}.
\end{align}
割愛するが, アイゼンシュタイン級数を低次のアイゼンシュタイン級数で表す系統的な方法は, ワイエルストラスのペー()関数の満たす微分方程式から得られる [梅村, Wiki2].
カスプ形式-ラマヌジャンのデルタ-判別式
定数項, の項が消えるように組み合わせるなら,
\begin{align}
E_4^3-E_6^2 = 1728q^2-41472q^4+\cdots
\end{align}
となる. 定数項がない, つまり に零点をもち, これはカスプ形式 cusp formと呼ばれている. 作り方から明らかなように, カスプ形式はウェイト12で初めて現れる.
ところで, テータ定数の組み合わせによっても保型形式が作られるのだった.
という, の生成元の作用による変換は, それぞれ次のようになっている.
テータ定数3つの積は保型形式ではない.
しかし, これを8乗するとウェイト12の保型形式になる.
これを -展開しよう. テータ定数の無限乗積展開から,
だから,
となる. この関数も明らかに最低次の項が で定数項がなく, カスプ形式になっている.
2通りの方法でカスプ形式が得られたが, 定数倍を除いて両者は一致していなくてはならない. の項が1になるように調整すると,
という関係が得られる. これがラマヌジャンのデルタ である.
ここで無限和表示の係数として導入された が有名なラマヌジャンのタウ関数 [Wiki3,OEIS]で, 無限乗積表示から明らかなように全て整数を取る.
上半平面上の点 と記号がバチバチに衝突しているが許してほしい.
最初のいくつかの値は以下のようになっている.
ここまでで, という, 2つのウェイト12の保型形式に代わって, 「ウェイト12の保型形式のなす空間」の基底として, という異なる組み合わせを得た. 両者の関係は,
\begin{gather}
E_{12} = \frac{441E_4^3+250E_6^2}{691},\ \ \ \Delta = \frac{E_4^3-E_6^2}{1728}\\
\Leftrightarrow E_{4}^3 = E_{12}+\frac{250\cdot 1728}{691}\Delta,\ \ \
E_6^2 = E_{12}-\frac{441\cdot 1728}{691}\Delta
\end{gather}
となっている.
なお, から生成される格子に対応するワイエルストラスのペー関数 の満たす微分方程式が,
と表されるとき,\begin{align}
g_2 &= 60G_4=\frac{4\pi^4}{3}E_4\\
g_3 &= 140G_6=\frac{8\pi^6}{27}E_6
\end{align}
だから, 判別式 discriminant について,
となっている.
qの2乗の項を消す?
さて, もうひとつ, と の特徴的な組み合わせを作れる. の項を消そう.
\begin{align}
E_4^3 &= 1+720q^2+179280q^4+\cdots,\\
E_6^2 &= 1-1008q^2+220752q^4+\cdots.
\end{align}
だったから, の の項を1に, の項を0にするためには,
\begin{align}
\alpha + \beta &= 1,\\
720\alpha -1008\beta &= 0,
\end{align}
であればよい. これを解いて,
\begin{align}
\alpha=\frac{7}{12},\ \beta=\frac{5}{12}
\end{align}
を得る. この係数による組み合わせを と表そう(記号の意味はあとで明らかになる).
ここで,
という記号を導入する. これらを使うと,
から,
と表される. はともに(整数係数)の項の和だから, の の係数もすべて整数になる. 課した制約は「の項が1, の項が0」だけだから, この性質は自明ではない.
上で見たように, と の代わりに と を使っても表現できて,
であることが分かる. さらに, と の -展開から,
という表示を得る. だから,確かに の項は消えている. 既に計算したが, の項の係数は,
から,
となる. に限らず, 上で示したように の係数は全て整数だから,
という合同式を得る. これはラマヌジャンが示したことであった.
さて, 係数が全て整数になるというこの は何だろう? 前の記事で述べたように,
なのであった [Conway]. ということは, 24次元偶モジュラー格子が存在すればそのテータ関数はウェイト12の保型形式になる.
逆に, テータ関数として を持つような格子があれば, その格子は偶ユニモジュラーで, しかもノルム2の点がない. 格子の理論ではノルム2の点のことをルート rootと呼ぶので, 言い換えれば, ルートを持たない24次元偶ユニモジュラー格子に対応するのがということになる.
回りくどくなってしまったが, 「ルートを持たない24次元偶ユニモジュラー格子」は存在して, それこそがリーチ格子なのである.
リーチ格子
リーチ格子にはいろいろな構成法がある. Wikipediaの記事にもたくさん載っている [Wiki4]. 八元数を使う方法は興味深い.
Conway-Sloaneの本 [Conway] の第24章は「リーチ格子の23の構成法」"Twenty-Three Constructions for the Leech Lattice"となっている*2.
ここでは, 拡張ゴレイ符号 extended Golay code [Wiki5]*3 を使ってリーチ格子を作ろう.
拡張ゴレイ符号
符号とは, 元来は情報の送受信のために利用される中で発生した概念だった. ここでは簡潔に, 2元体上のベクトル空間 から それより大きな への写像を与えるものだということだけ確認しておく. 特にそのうち線形なものを線形符号と呼び,
\begin{align}
\mathbb{F}_2^m\ \xrightarrow{} \mathbb{F}_2^n\ :\ b\mapsto bG
\end{align}
によって与えられるとき, 行列 のことを生成行列 generating matrix, による の像全体のことを符号語 code wordと呼ぶ.
以下で説明するレシピに従って, Pythonで拡張ゴレイ符号を扱おう.
最初にNumpyとMatplotlibをインポートしておく.
import numpy as np import matplotlib.pyplot as plt
まず正20面体の隣接行列 adjacent matrixを用意する. 正20面体の12個の頂点に番号を振り, 頂点どうしが辺で結ばれていれば1, 結ばれていなければ0 とする. 正20面体をつくるところから始める.
phi = (1+np.sqrt(5))/2.0 Icos = [[1,phi,0],[1,-phi,0],[-1,phi,0],[-1,-phi,0], [0,1,phi],[0,1,-phi],[0,-1,phi],[0,-1,-phi], [phi,0,1],[-phi,0,1],[phi,0,-1],[-phi,0,-1]] #正20面体の12個の頂点 A = np.zeros((12,12), dtype=int) #正20面体の隣接行列をつくる for i in range(12): for j in range(12): A[i][j] = int(phi-0.1 < np.inner(Icos[i],Icos[j]) < phi+ 0.1) print(A)
[[0 0 1 0 1 1 0 0 1 0 1 0] [0 0 0 1 0 0 1 1 1 0 1 0] [1 0 0 0 1 1 0 0 0 1 0 1] [0 1 0 0 0 0 1 1 0 1 0 1] [1 0 1 0 0 0 1 0 1 1 0 0] [1 0 1 0 0 0 0 1 0 0 1 1] [0 1 0 1 1 0 0 0 1 1 0 0] [0 1 0 1 0 1 0 0 0 0 1 1] [1 1 0 0 1 0 1 0 0 0 1 0] [0 0 1 1 1 0 1 0 0 0 0 1] [1 1 0 0 0 1 0 1 1 0 0 0] [0 0 1 1 0 1 0 1 0 1 0 0]]
なにをしているかというと, として,
の12点が正20面体の12個の頂点をなして, 内積が に等しければ(計算上では0.1の誤差を許している)辺で結ばれ, そうでなければ結ばれないことを利用している.
拡張ゴレイ符号の生成行列は, 12次単位行列 とを反転したもの(0と1を入れ替える;で表す)をつなげて, で得られる.
E = np.identity(12,dtype=int) G= np.concatenate([E, (A+1)%2], axis=1) #ゴレイ符号の生成行列
1を白, 0を黒となるように画像としてこの生成行列の形を見よう.
plt.imshow(G, cmap = 'gray')
plt.show()
ここでbin_to_vecという関数を定義する.
def bin_to_vec(n, length): #nを2進法で表して, 長さlength のベクトルに変換 return np.array([int(x) for x in format(n,'0{}b'.format(length))], dtype=int)
これを使って,0から2^12-1=4097までの整数をベクトルに変換する.
X = np.array([bin_to_vec(n,12) for n in range(2**12)]) print(X)
[[0 0 0 ... 0 0 0] [0 0 0 ... 0 0 1] [0 0 0 ... 0 1 0] ... [1 1 1 ... 1 0 1] [1 1 1 ... 1 1 0] [1 1 1 ... 1 1 1]]
X は長さ12の横ベクトルを4096行並べたもの.
print(X.shape)
(4096, 12)
右から生成行列Gをかけて, 拡張ゴレイ符号の符号語全体を作る.
C = (X@G)%2 #符号語全体 print(C) print(C.shape)
[[0 0 0 ... 0 0 0] [0 0 0 ... 0 1 1] [0 0 0 ... 1 1 1] ... [1 1 1 ... 0 0 0] [1 1 1 ... 1 0 0] [1 1 1 ... 1 1 1]] (4096, 24)
2元符号において, 成分に持つ1の数を重み weightという*4. たとえば, (1,1,0,0,1) は1が3個あるので重み3. 拡張ゴレイ符号の符号語の重みの分布がどうなっているか見よう.
W = np.sum(C,axis=1) for n in range(min(W),max(W)+1): k = len(W[W==n]) if(k > 0): print(n,k)
0 1 8 759 12 2576 16 759 24 1
表にするとこうなる.
拡張ゴレイ符号の符号語の重み
重み | 0 | 8 | 12 | 16 | 24 |
---|---|---|---|---|---|
符号語の数 | 1 | 759 | 2576 | 759 | 1 |
あきらかに重み1に対応するのは (0,0,…,0). その次に小さい重み(最小重み)が8となっている. これが拡張ゴレイ符号のもつ著しい性質.
符号語同士の間にはハミング距離 Hamming distanceが定義される. ふたつの符号語間のハミング距離は, 同じ位置にあって違う成分の数である. 線形符号だから, 異なる2つの符号語間の最小の距離は最小の重みに等しい. 従って, 拡張ゴレイ符号において, ある符号語から最も近い別の符号語までの距離は8であって, 非常にうまく符号語どうしが散らばっていることが分かる. これは誤り訂正符号 error correcting code としての優秀さを示している.
このことは, 拡張ゴレイ符号からつくられるリーチ格子が示す良い性質にも遺伝する.
リーチ格子をつくる
いよいよ拡張ゴレイ符号を使ってリーチ格子をつくる. もっとも分かりやすい定義は次のようになる [Conway, Chap4].
これが確かに格子をなすことを見るには, ふたつの点をとって差がまたこの集合に入ることを示せばよい(略).
が厄介なので, を考える. ノルム を非零で最小にする点を調べよう. 符号語 の重みを で表す.
(A) のタイプ.
( A-i ) のとき, は成分のなかでふたつだけ で他の22成分は0なら, ノルムは最小で32になる.
という形をしていて, 個ある.
( A-ii ) のとき, の非零成分のうち偶数個に対して, 同じ位置で をとって, 他は0となるような でノルムは最小で32になる.
つまり, のような形で, 非零成分は符号語の1の位置に対応, 負符号の数は偶数個.
重み8の符号語759個のそれぞれに対して 個あるので,
あわせて 個.
( A-iii ) のとき、どんな をとっても は16個以上の成分が になり, ノルムは最小で64をとる.
( B ) のタイプ.
すべての成分が奇数だから, ノルムは必ず24以上. さらに, となるためには でなくてはならないことが分かるが, これはの成分の和が奇数であることに反する. 従ってノルムは24にはなりえない. また,
\begin{align}
\|\sqrt{8}\,l_1\|^2 = 16\|y\|^2 + 24 + 16(c,y) +8{\rm wt}(c) + 8\sum_{i=1}^{24}y_i
\end{align}
から, このノルムは8の倍数になるため, 最小でも32. ノルムが32となるためには, 1つの成分だけ , 他は という形でなくてはならないが, とその成分の位置を入れ替えたベクトルに対して, 符号の0に対応する位置で複号の上をとり, 1に対応する位置で下をとるならこれは許される. すべての符号語に対して24個ずつ対応するから, あわせて個ある.
まとめると,
- 型 : 1104個
- 型 : 97152個
- 型 : 98304個
が において最小ノルム32のベクトルの全てで, あわせて 個ある.
この議論で分かったことは, が零ベクトルを除いてノルムが32より小さいベクトルを持たないということであった. 元に戻ると, が零ベクトルを除いてノルムが4より小さいベクトルを持たないということになる.
仮に上から順に3つのタイプをそれぞれ40型, 20型, 31型と呼ぶことにしよう.
既に用意していた拡張ゴレイ符号の符号語を使って, これらを列挙しよう.
L = [] #40型 count = 0 for i in range(24): for j in range(i+1,24): for k in [(1,1),(1,-1),(-1,1),(-1,-1)]: v = np.zeros(24,dtype=int) v[i] = k[0] * 4 v[j] = k[1] * 4 L.append(v) count += 1 print('40-type : ', count) #20型 count = 0 for i in range(2**8): s = -2 + 4 * bin_to_vec(i,8) if(sum(s) % 8==0): for c in C: if(sum(c) == 8): a = np.arange(24)[np.array([int(x) for x in c])==1] v = np.zeros(24,dtype=int) for i,x in enumerate(a): v[x] = s[i] L.append(v) count += 1 print('20-type : ', count) #31型 count = 0 for i in range(24): v = np.ones(24,dtype=int) v[i] = -3 for c in C: s = [1-2*int(x) for x in c] L.append(v*s) count += 1 print('31-type : ', count) L = np.array(L) print('total : ',len(L))
40-type : 1104 20-type : 97152 31-type : 98304 total : 196560
ノルム32のベクトルをndarray Lに格納.
L[0] : (4,4,0,0,...,0) と他の元との内積を調べてみる.
P = L @ L[0] for n in range(min(P), max(P)+1): k = len(P[P==n]) if(k > 0): print(n,k)
-32 1 -16 4600 -8 47104 0 93150 8 47104 16 4600 32 1
結果はL[0]との内積が-32の点はLの中に1個, -16の点は4600個, -8の点は47104個,...あるということを意味している. 全て8の倍数だから, に戻って考えると, この範囲では内積が整数になっているということになる.
前回の記事で -格子に対して行ったように, ベクトルたちをソートする.
N = [int(''.join(list(map(str,v)))) for v in L+4] N_sort = sorted(N) L_sort = -4 + np.array([np.array([int(x) for x in str(n).zfill(24)]) for n in N_sort]) print(L_sort)
[[-4 -4 0 ... 0 0 0] [-4 0 -4 ... 0 0 0] [-4 0 0 ... 0 0 0] ... [ 4 0 0 ... 0 0 0] [ 4 0 4 ... 0 0 0] [ 4 4 0 ... 0 0 0]]
説明は省略するが, やはり前回と同じ方法で, 基底を選び取る.
D = L_sort[196560//2:] #正ベクトルが小さいものから順に B = np.array([D[0],D[1]]) #部分基底 Gram = B@B.T #部分基底のグラム行列 Gram_inv = np.linalg.inv(Gram) #グラム行列の逆行列 for i,v in enumerate(D): q = (Gram_inv@(B@v.T))@B r = np.linalg.norm(v-q) if(r > 0.0001): B = np.concatenate([B, np.array([v])],axis=0) Gram = B@B.T Gram_inv = np.linalg.inv(Gram) print(B) print(B.shape)
[[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 -4] [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 4] [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 -4 0] [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 -4 0 0] [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 -4 0 0 0] [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 -4 0 0 0 0] [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 -4 0 0 0 0 0] [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 -4 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 -4 0 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 -4 0 0 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0 0 0 0 0 0 4 -4 0 0 0 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0 0 0 0 0 4 -4 0 0 0 0 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0 0 0 0 2 -2 -2 0 0 -2 0 -2 0 -2 0 -2 2] [ 0 0 0 0 0 0 0 0 0 0 2 -2 -2 -2 -2 -2 0 0 0 0 -2 2 0 0] [ 0 0 0 0 0 0 0 0 0 2 -2 -2 0 0 -2 -2 0 -2 0 -2 0 0 2 0] [ 0 0 0 0 0 0 0 0 2 -2 -2 -2 0 0 0 0 0 0 0 0 -2 -2 -2 2] [ 0 0 0 0 0 0 0 2 -2 -2 -2 0 0 -2 -2 0 0 0 0 -2 2 0 0 0] [ 0 0 0 0 0 0 2 -2 -2 -2 -2 -2 0 0 0 0 -2 2 0 0 0 0 0 0] [ 0 0 0 0 0 2 -2 -2 -2 -2 0 0 -2 0 -2 0 0 0 2 0 0 0 0 0] [ 0 0 0 0 2 -2 -2 -2 -2 0 -2 0 0 0 0 0 0 0 0 0 -2 0 2 0] [ 0 0 0 2 -2 -2 -2 0 0 0 -2 0 0 0 -2 0 0 0 -2 0 0 0 0 2] [ 0 0 2 -2 -2 -2 -2 -2 0 0 0 0 -2 2 0 0 0 0 0 0 0 0 0 0] [ 0 2 -2 -2 -2 -2 0 0 0 0 0 0 0 0 -2 0 0 0 0 0 -2 0 2 0] [ 1 -3 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 1 1 1 1 1 1 -1 -1 -1 -1]] (24, 24)
最終的に得られた行列Bは基底をなすベクトルを横ベクトルとして上から順に並べたもの.
正ベクトルを並べた行列Lに左からBの逆行列をかけてやると, この基底上での成分が得られる. さらにそれを整数に丸める.
Coef = np.array(np.round(D@np.linalg.inv(B)),dtype=int) print(Coef)
[[ 1 0 0 ... 0 0 0] [ 0 1 0 ... 0 0 0] [ 0 0 1 ... 0 0 0] ... [ 8992 7073 14632 ... 8 6 4] [ 9556 7517 15550 ... 10 6 4] [10535 8287 17144 ... 10 8 4]]
表示されているのは一部分だが, かなり大きい値が出ていることに注目.
丸めた係数による基底のベクトルたちの線形結合が, もとのベクトルたちに一致するか見る.
R = D-Coef@B print(R) print(len(R[R!=0]))
[[0 0 0 ... 0 0 0] [0 0 0 ... 0 0 0] [0 0 0 ... 0 0 0] ... [0 0 0 ... 0 0 0] [0 0 0 ... 0 0 0] [0 0 0 ... 0 0 0]] 0
最後の行の0が, 実はCoefが丸めなくても正確に整数成分であったことを意味している.
これによって, Bは正しく格子の -基底になっていることが確認できた.
さらに, Coefはすべての成分が非負であることも確認できる.
print('+ :', len(Coef[Coef > 0])) print('0 :', len(Coef[Coef==0])) print('- : ',len(Coef[Coef < 0]))
+ : 2258297 0 : 100423 - : 0
ここでは正ルートの中から単純ルートを選び取るのと同じ方法を使ったが, この方法で基底が得られることが保証されているのか分からない*5. が, とにかくノルム32のベクトルたちの中から基底を取ることができた.
のグラム行列であるGramの成分は全て8の倍数なので,
print(len(Gram[Gram%8!=0]))
0
それを8で割ればすべての成分が正しく整数になっている, のグラム行列が得られる.
print(Gram//8)
[[ 4 0 -2 0 0 0 0 0 0 0 0 0 -2 0 1 -2 0 0 0 1 -1 0 1 0] [ 0 4 -2 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 0 1 -1] [-2 -2 4 -2 0 0 0 0 0 0 0 0 1 1 -1 0 0 0 0 -1 0 0 -1 0] [ 0 0 -2 4 -2 0 0 0 0 0 0 0 -1 -2 0 0 1 0 0 -1 0 0 -1 0] [ 0 0 0 -2 4 -2 0 0 0 0 0 0 1 1 -1 1 -2 0 0 1 0 0 1 1] [ 0 0 0 0 -2 4 -2 0 0 0 0 0 -1 0 1 0 1 0 1 0 -1 0 0 0] [ 0 0 0 0 0 -2 4 -2 0 0 0 0 1 0 -1 0 0 1 -1 0 1 0 0 0] [ 0 0 0 0 0 0 -2 4 -2 0 0 0 -1 0 1 0 0 -2 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 -2 4 -2 0 0 1 -1 -1 0 0 1 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0 -2 4 -2 0 0 0 0 0 -1 0 -1 0 -1 0 -1 0] [ 0 0 0 0 0 0 0 0 0 -2 4 -2 -1 0 1 0 0 0 1 0 1 1 1 -1] [ 0 0 0 0 0 0 0 0 0 0 -2 4 0 0 0 0 1 0 -1 0 0 -2 0 0] [-2 0 1 -1 1 -1 1 -1 1 0 -1 0 4 1 -1 1 0 0 0 0 1 0 0 0] [ 0 0 1 -2 1 0 0 0 -1 0 0 0 1 4 1 0 0 0 1 0 0 0 1 0] [ 1 1 -1 0 -1 1 -1 1 -1 0 1 0 -1 1 4 0 1 0 0 1 1 0 1 -1] [-2 0 0 0 1 0 0 0 0 0 0 0 1 0 0 4 0 1 0 0 1 0 0 1] [ 0 0 0 1 -2 1 0 0 0 -1 0 1 0 0 1 0 4 1 1 0 1 -1 0 0] [ 0 0 0 0 0 0 1 -2 1 0 0 0 0 0 0 1 1 4 1 1 0 0 0 1] [ 0 0 0 0 0 1 -1 0 0 -1 1 -1 0 1 0 0 1 1 4 1 0 1 0 1] [ 1 1 -1 -1 1 0 0 0 0 0 0 0 0 0 1 0 0 1 1 4 1 1 1 1] [-1 1 0 0 0 -1 1 0 0 -1 1 0 1 0 1 1 1 0 0 1 4 1 1 0] [ 0 0 0 0 0 0 0 0 0 0 1 -2 0 0 0 0 -1 0 1 1 1 4 1 1] [ 1 1 -1 -1 1 0 0 0 0 -1 1 0 0 1 1 0 0 0 0 1 1 1 4 0] [ 0 -1 0 0 1 0 0 0 0 0 -1 0 0 0 -1 1 0 1 1 1 0 1 0 4]]
対角成分が全て偶数の対称行列になっているから, が偶格子であることが分かる.
また, 行列式を計算すると,
print(np.linalg.det(G//8))
0.9999999999999982
誤差の範囲で1に一致する. 従って, の行列式は1で, これは格子のユニモジュラー性を意味する.
ノルムの最小値に関して議論したことを合わせて, 結局, 次の事実が確認できた.
ルートを持たない24次元偶ユニモジュラー格子は, 存在すればそのテータ関数が上で定義した になるので, 自動的にリーチ格子のテータ関数が決まった.
まとめと展望
ウェイト の保型形式のなす空間は, 小さいほうの から数えると初めて2次元になる. このことから, それぞれ異なった特徴を持つ, 3つのウェイト12の保型形式 を, の和として表した.
偶ユニモジュラー格子は8の倍数次元にしか存在しないのだった. 本記事では, の項が消えるようなウェイト12の保型形式を探し, それに対応する24次元偶ユニモジュラー格子がリーチ格子であることを見た.
より一般には, アイゼンシュタイン級数を適当に組み合わせることで, 定数項1を除いて, のなるべく小さい次数が消えるような保型形式を作ることが考えられる. 少し考えると, ウェイト の保型形式は, 最良で の最小次数が
となるように組み合わせられることが分かる. の場合がで, 確かに が最小次数になっている.
このようにして作られた保型形式をテータ関数に持つ 次元偶ユニモジュラー格子は極値 extremal 偶ユニモジュラー格子と呼ばれる. - 格子と リーチ格子はともに極値偶ユニモジュラー格子の例である.
極値偶ユニモジュラー格子はいくつ存在するか, というのは非常に難しい問題らしく, 次元までは存在が知られていて, 72次元では存在するかどうか未確定だった, というのが2010年以前の状況. この年 Gabriele Nebeによって72次元での極値偶ユニモジュラー格子が構成されたこと [Nebe1] は, かなりの驚きをもって迎えられたらしい. ということが, 数学セミナー2012年1月号の記事 [原田] に書かれている. ちなみにこの号の特集タイトルは「想定外の数学」である. それより少しあとの話題を含む解説がNebe本人によって上げられていた [Nebe2].
8, 24次元での最密球充填を与えるのが(「不規則な」配置を含めても) 各々-格子 [Viazovska]とリーチ格子 [Cohn]であることが証明されたのも本当にごく最近のことだ. 今の自分に届くのはほんのお話程度の領域でしかないけれど, それでもこうして少し手を動かすと, いかに感動的な出来事が現在起こっているかより身近に感じとれるような気がする.
リファレンス
書籍
[Conway] J. H. Conway, N. J. A. Sloane, Sphere Packings, Lattices and Groups, Springer-Verlag, 1999.
[梅村] 梅村浩, 『楕円関数論―楕円曲線の解析学』, 東京大学出版会, 2000.
[Koblitz] N. Koblitz, Introduction to Elliptic Curves and Modular Forms, Springer-Verlag, 1993.
(N. コブリッツ, 上田勝, 浜畑芳紀訳, 『楕円曲線と保型形式』, 丸善出版, 2016.)
[志賀] 志賀弘典, 『保型関数-古典理論と現代的応用』, 共立出版, 2017.
[原田] 原田昌晃, 「72次元の極値偶ユニモジュラー格子の存在について」, 『数学セミナー』2012年1月号, 日本評論社.
オンライン
[Nebe1] G. Nebe, "An even unimodular 72-dimensional lattice of minimum 8", arXiv: 1008.2862, 2010.
https://arxiv.org/abs/1008.2862
[Nebe2] http://www.math.rwth-aachen.de/~Gabriele.Nebe/talks/OW12.pdf
[Viazovska] M. Viazovska, "The sphere packing problem in dimension 8", 2016, https://arxiv.org/abs/1603.04246
[Cohn] H. Cohn, A. Kumar, S. D. Miller, D. Radchenko, M. Viazovska, "The sphere packing problem in dimension 24",2016 https://arxiv.org/abs/1603.06518
[Wiki1] Bernoulli number - Wikipedia
[Wiki2] Eisenstein series - Wikipedia
[Wiki3] Ramanujan tau function - Wikipedia
[Wiki4] Leech lattice - Wikipedia
[Wiki5] Binary Golay code - Wikipedia
[OEIS] Ramanujan's tau function (or Ramanujan numbers, or tau numbers).A000594 - OEIS
- What is the easiest way to describe the Leech lattice explicitly? - Mathematics Stack Exchange
- Sphere Packing http://pantodon.shinshu-u.ac.jp/topology/literature/sphere_packing.html
- "Out of a Magic Math Function, One Solution to Rule Them All" Quanta Magazine
- Leech lattice | Complex Projective 4-Space
- tsujimotter.hatenablog.com
- integers.hatenablog.com