bitterharvest’s diary

A Bitter Harvestは小説の題名。作者は豪州のPeter Yeldham。苦闘の末に勝ちえた偏見からの解放は命との引換になったという悲しい物語

集合から圏論へ:ミクロな世界からの宇宙への旅立ち(1)

3.集合の世界から圏論の世界へ

ごちゃごちゃしたミクロの世界をかなぐり捨てて雄大なマクロの世界(宇宙)へ旅立つことにしよう。数学でのミクロな世界は集合で、雄大な宇宙は圏論である。集合は、要素の集まりとして定義される。また、集合を論じるときは、それと一緒に関数(あるいは写像)も学んだことと思う。関数は二つの集合を結びつけるものだ。

圏論では、集合に相当するものを対象(object)と呼び、関数に相当するものを射(morphism)と呼ぶ。集合を論じるときは、ミクロな世界なので、その中に含まれている要素がとても重要な役割をする。しかし、圏論では、マクロな世界を論じるので、対象の中身は気にしないことにする。Haskellでは、対象をタイプとして表すが、常に、タイプを気にし、その構成要素に対してはたいして注意を払わない。

今回の記事では、集合を捨てて対象に、関数を捨てて射に、乗り移れるための準備をしよう(Haskellでは、射を関数と呼んでいるので、少しごちゃごちゃするのだが、Haskellで関数といっているときは、圏論での射と思って欲しい)。

3.1 関数とは

物事を始めるにあたっては、その中で用いる用語の意味をはっきりさせておく必要がある。そこで、ここでは、関数とは何かをはっきりさせておこう。これは、射との違いを考えるうえで助かるはずだ。

少し、基礎的な概念から始めてみよう。プログラムを書くからには、そこに何らかの意思あるいは意図があるはずである。出来上がったプログラムは、そこに、作者の意思あるいは意図を実現しているはずである。そこで、プログラムを書く際に、どのようにして、意思や意味を埋め込んでいくのかが重要になる。

JavaやCのような命令型(imperative)のプログラミング言語を用いれば、意思や意図は、「プログラムのシークエンス(how)」として埋め込まれることになる。HaskellPrologのような宣言型(declarative)のプログラミング言語を用いれば、それらは「どのようなものであるか(what)」として埋め込まれることになる。

宣言型の説明のところが分かりにくいので、補足しておいた方がよいと思う。プログラムはある特定の領域あるいは特定の問題を対象にして記述される。特定の領域には、それを細かく分解していくと、それ以上は分解できない構成要素(atom)が出てくる。そこで、構成要素の性質あるいは性格をプログラムとして記述する。分解したのとは逆に構成要素を用いて組み立てていくと元の領域に戻るはずである。この作業は構成要素を記述したプログラムの合成で実現する。

例えば、量子力学の世界を考えると、最小の単位は粒子である。これは、数学的にはケットとブラと表すことができる。そこで、ケットとブラを対象(データ型)として表す。ケットとブラに成り立っている関係を射(関数)とその合成で表すことで、量子力学の世界をプログラムで提供できるようになる。

通常、コンピュータ科学の世界では、意思や意図は、意味という。命令型のプログラミング言語を用いて意味を表す方法を操作的意味論(operational semantics)といい、宣言型に対しては表示的意味論(denotational semantics)という。表示的という言葉が使われているのは、構成要素をその特定領域での言葉を用いて表現しているということによる。

1)関係

それでは、表示的意味論での実際的な例を考えることにしよう。代表的なものは、そして、最も多くの場面で使われるのは、関係(relation)だろう。通常の文章でもよく出てくるし、コンピュータの世界でも、データベースでは基本的な概念になっている。例えば、合コンをした時に、望ましいと思った異性をあげることは一つの関係を表している。

ある男性は、一人の女性を望ましいと思ったかもしれない。また、別の男性は、気が多いのか、複数の女性を考えたかもしれない。また、さらに別の男性は、好みがうるさいのか、誰もいなかったというかもしれない。逆に女性側から見たときも、同じようなことが起こる。とても運がよい場合には、二人の好みが合致する場合もある。このような関係を示したのが次の図だ。
f:id:bitterharvest:20161102151424p:plain

数学では、関係は直積集合(Cartesian product)で表される。例えば、次のようになる。男性の方から女性を望ましいと思う関係は、例えば、次のように表される。
\begin{eqnarray}
(Peter, Marry) \\
(Peter, Betty) \\
(Mike, Ellen) \\
(Tom, Michell)
\end{eqnarray}

コンピュータでは、関係は、多くの場合、前述したようにデータベースとして表される。

2)関数

関係は、とても一般的で、二つのグループの関わり合いを形式的に記述するのに都合がよい。しかし、あまりにも自由度が大きすぎるため、数学的な枠組みで使おうとするときは、もう少し、制限の強い概念を用いたほうが都合のよいことが多い。これが関数と呼ばれる。関数は、ドメイン(domain)とコドマイン(codomain)の関係を表すが、そこには、いくつかの約束事がある。
1) 関数には方向性がある。ドメインとコドメインの関係は対等ではなく、矢印がドメインからコドメインの方に向かっている。
2) ドメインの全ての要素に対して、コドマインのある一つの要素が対応する。先ほどの合コンでの例で、好ましい女性がいなかったという男性がいたが、このようなことは許されず、全ての男性は好ましい女性がいなくてはならない。また、複数の女性を好ましいと思う男性がいたがこのようなことも許されない。但し、一人の女性に対して、複数の男性が好ましいと思うこと(多対一対応)は許される。
f:id:bitterharvest:20161102162603p:plain

次の例は、全ての男性が女性一人だけを望ましいといっているので、この関係は関数となる。コドメインの中で、相手のある要素の集まりをイメージという。これらの関係を図で示すと次のようになる。
f:id:bitterharvest:20161102163604p:plain
ドメインの全ての要素に対して相手が割り当てられているような関数を全関数(total function)という。これに対して、ドメインのあるものに対しては相手が割り当てられていないような関数を部分関数(partial function)という。これから扱うすべての関数は全関数である。関数でないものの例を以下に示す。
f:id:bitterharvest:20161103153858p:plain

また、関数の値は変わることがない。このような関数を純粋関数(pure function)という(日本語では純粋関数という用語は使わないようだがここでは便宜的に使う。純粋関数型言語という使い方はあるが、それ以外で使われる例はないようである)。人間の脳は純粋関数ではない。相手に対する好き嫌いは、様々な経験を通して、変化することが往々にしてある。このため、一定であるという保証をすることはできない。一般に、記憶を持つようなものは純粋関数にはならない。

3)逆関数

先ほどは、男性の方から好ましい女性を示す関数\(f\)について説明した。それでは、女性の方から好ましい男性を示す関数\(g\)を考えてみよう。

そこで、関数\(f\)を使って望ましい女性を得た後、関数\(g\)を使ってその女性が、好ましいと思っている男性を求めてみることにしよう。もし、その結果が自身に戻ってくるんであれば、お互いに好ましいと思っているので、こんなに嬉しいことはない。もしすべての男性について、自身に戻ってくるようであれば、男性たちはとても幸せだろう。

逆に、女性の方からも考えてみよう。その場合には、関数\(g\)を施した後で、関数\(f\)を施すことになる。やはり、全ての女性について、自身に戻ってくるようであれば、女性たちはとても幸せだろう。

このようなことが、男性にも、女性にも起こっているとすると、この合コンは理想的な結果をもたらしたといえるだろう。

このことを数学的に記述すると、男性の場合には、
\begin{eqnarray}
g \circ f = id_M
\end{eqnarray}
女性の場合には、
\begin{eqnarray}
f \circ g = id_F
\end{eqnarray}
となる。ここで、\(id_M\)は男性のグループに対する恒等写像、\(id_F\)は女性のグループに対する恒等写像である。

圏論での用語を用いて上記を記述しなおすと、
1) 対象:\(M\)(男性のグループ)、\(F\)(女性のグループ)
2) 射:\(f:M \rightarrow F\)(好ましい女性への射)、\(g:F \rightarrow M\)(好ましい男性への射)
3) 恒等射:\(id_M\)(男性のグループでの恒等射)、\(id_F\)(女性のグループでの恒等射)
4) 合成:\(g \circ f\)、\(f \circ g\)

圏論の世界での同型写像の定義】
一般に、
\begin{eqnarray}
f:M &\rightarrow & F\\
g:F &\rightarrow & M\\
g \circ f &=& id_M \\
f \circ g &=& id_F
\end{eqnarray}
が成り立つとき、\(f\)と\(g\)は同型写像であるという。数学の概念の中では、重要な概念の一つである。

ここでのポイントは、合成\(\circ\)と恒等射\(id\)を用いて、同型写像が定義されていることである。

上記では、\(M,F,f,g\)について具体的な例を挙げて説明したが、これらはどのようなもので置き換えてもよい。その時、上記のような関係が成り立つとき、\(f\)と\(g\)は同型写像という。また、\(M\)と\(F\)は同型という。

圏論による同型写像の定義の重要性は、対象の要素については何も問うていないことである。細かいことは気にしなくてもよいといっている。また、写像が射に変わっている点である。射は写像を超えるもっと普遍的な概念である。このため、より抽象的な同型を定義するのに役立つ(後々、関手(functor)の話をするがこれは写像をより一般化したものである)。

同型写像は、対象同士が対称(symmetric)であると教えてくれる。あるいは、行って戻れる、すなわち、可逆的(invertible)であるとも知らせてくれる。

同型写像は変化のないモノトーンの世界なのだが、世の中でこのようなことが成り立つことは少ない。同型写像にならない条件は次の通りである。
1) 対象のサイズが異なる。先の合コンの場合、男性と女性の数が異なる場合には、1対1のペアを作ろうとしても、数の多い性の人の誰かが余ってしまう。
2) 多対一対応のものがある。即ち、コドメインのある要素が、二つ以上のドメインの要素から写像される。即ち、\(\exists a, \exists b, a \neq b, f(a) = f(b)\)である。この場合には、二人以上の男性からある女性が望ましいと思われても、その女性は一人の男性しか望ましいと思えないので、破綻してしまうこととなる。

圏論の世界での逆写像の定義】
関数\(f\)と\(g\)が同型写像である時、\(g\)は\(f\)の逆関数であるという。\(f\)の逆関数は\(f^{-1}\)と書く。同様に、\(f\)は\(g\)の逆関数であるという。

逆関数の例を挙げておこう。今、自然数(0から始まる)と奇数(1から始まる)のグループを考えることにする。自然数のグループの方が奇数のそれよりも2倍だけサイスが大きいと考えがちだが、そんなことはない。
f:id:bitterharvest:20161103105346p:plain
自然数\(x\)から奇数\(y\)への写像\(f\)を\(y=f(x)=2x+1\)とし、奇数から自然数への写像\(g\)を\(x=g(y)=(y-1)/2\)とすると、
\begin{eqnarray}
g \circ f &=& id_\mathbb{ N } \\
f \circ g &=& id_{Odd}
\end{eqnarray}
が成り立つ。

このことから、自然数と奇数は同型であることが分かる。奇数は自然数の部分集合だと思うとなんか変な気分になる。要素の数が無限の時、往々にして、直感が働かない時があるが、これはその例の一つである。

4)全射単射

圏論の世界から、もう一度、集合の世界に戻ってみよう。この世界では、集合の要素同士がどのように写像されているかが、重要な話題だ。その中から、全射(surjective)と単射(injective)に登場してもらおう。

Surjectiveという単語は、日常会話で用いることはまずないので、初めて見たという人も多いことと思う。この単語は、surとjectiveをつなげたもので、surは、surfaceからも連想できるように、「上」という意味を持つ。語源は古フランス語だ。また、jectは「投げる(の過去分詞)」を意味し、ラテン語を語源としている。

従って、Surjectiveは形容詞「上に投げられた」となる。これに対応した全射という日本語訳もあまり好きではないのだが、関数\(f:A \rightarrow B\)のイメージ\(f(A)\)とコドメイン\(B\)が一致するとき、即ち\(f(A) = B\)のとき、関数\(f\)は全射であるという。

Injectiveは同じようにinとjectiveの合成語である。inは「中へ」という意味だが、この場合には、「そのまま中へ」という意味が含まれているのであろう。別の要素は別の要素へ移すというニュアンスで用いられている。従って、ドメインの任意の二要素\(a,b\)に対して\(a \neq b\)の時、\(f(a) \neq f (b) \)が成り立つならば、関数\(f\)は単射であるという。

単射全射の例をいくつか示しておこう。
f:id:bitterharvest:20161103153213p:plain

集合の世界では、この二つの用語を用いて、先ほどの同型写像が定義される。

【集合の世界での同型写像の定義】
関数\(f:A \rightarrow B \)が、全射単射である時、\(f\)は同型写像であるという。

【集合の世界での逆写像の定義】
関数\(f\)が同型写像である時、その矢印の向きを反対にした写像を\(f\)の逆写像という。これを\(f^{-1}\)と記す。

皆さんは、集合の上に定義された同型写像と、圏論の上でのそれとではどちらが好きだろうか。集合の上での定義はアセンブリ言語による定義で、圏論の上での定義は高級言語でのそれに相当すると思うのだが、どうだろうか。

5)エピ射とモノ射

同型写像と逆写像圏論の世界と集合の世界でそれぞれ定義したので、全射単射についても圏論の世界で定義してみよう。

まず、全射の方から始める。集合の世界はラテン系の言語を語源としていたので、圏論ではギリシャ系の語源を用いることで、用語も変えてみることとする。ラテン系のチャラチャラした世界からギリシャ系の哲学の世界へ移ろうというと物議を醸しそうだが、気分としてはそんなところだ。surに対応するギリシャ語はepiである。そこで、全射をエピ射(epimorphism)ということにしよう。

また、単射の方は、単一を表すギリシャ語monoを用いてモノ射(monomorophism)ということにする。

それでは、エピ射を定義してみよう。

エピ射でないとすると下図のようなことが発生する。
f:id:bitterharvest:20161103165130p:plain

今、射\(f:A \rightarrow B \)がエピ射でないとする。この時、
\begin{eqnarray}
(g_1 \circ f = g_2 \circ f) \land (g_1 \neq g_2)
\end{eqnarray}
を成り立たせるある対象\(C\)と二つのある射\(g_1: B \rightarrow C\)と\(g_2: B \rightarrow C\)が存在する。
(上の記述を論理式で念のために表しておこう。
\begin{eqnarray}
\exists C,\exists g_1(g_1: B \rightarrow C),\exists g_2(g_2: B \rightarrow C) \\
f(f:A \rightarrow B) \notin Epimorphisms \Rightarrow \\
(g_1 \circ f = g_2 \circ f) \land (g_1 \neq g_2)
\end{eqnarray}
)

そこで、この対偶を取ってみよう。次のようになる(飛躍しすぎだと思う人は、上記の論理式より導き出してほしい)。
全ての対象\(C\)と全ての二つの射\(g_1\)と\(g_2\)に対して
\begin{eqnarray}
(g_1 \circ f \neq g_2 \circ f) \lor (g_1 = g_2)
\end{eqnarray}
の時、射\(f:A \rightarrow B \)はエピ射となる。

そこで、
\begin{eqnarray}
(g_1 \circ f \neq g_2 \circ f) \lor (g_1 = g_2)
\end{eqnarray}
の部分は次のように変えることができる。
\begin{eqnarray}
(g_1 \circ f = g_2 \circ f) \Rightarrow (g_1 = g_2)
\end{eqnarray}
これを利用して次のように定義することができる。

【エピ射の定義】
射\(f:A \rightarrow B \)が次の条件を満たす時、エピ射という。
条件:全ての対象\(C\)と全ての二つの射\(g_1: B \rightarrow C\)と\(g_2: B \rightarrow C\)に対して、
\begin{eqnarray}
g_1 \circ f = g_2 \circ f
\end{eqnarray}
が成り立つとき、
\begin{eqnarray}
g_1 = g_2
\end{eqnarray}
である。[条件終了]


モノ射については、同じように考える。
f:id:bitterharvest:20161104000815p:plain

今、射\(f:A \rightarrow B \)が上図に示すようにモノ射でないとする(上図では異なる矢印のみを記してある。記してない部分は、二つの矢印とも同じ場所から出発して同じ場所にたどり着くので、適当に補って考えること)。この時、
\begin{eqnarray}
(f \circ g_1 = f \circ g_2) \land (g_1 \neq g_2)
\end{eqnarray}
を成り立たせるある対象\(C\)と二つのある射\(g_1: C \rightarrow A\)と\(g_2: C \rightarrow A\)が存在する。

そこで、この対偶を取る。次のようになる。
全ての対象\(C\)と全ての二つの射\(g_1\)と\(g_2\)に対して
\begin{eqnarray}
(f \circ g_1 \neq f \circ g_2) \lor (g_1 = g_2)
\end{eqnarray}
の時、射\(f:A \rightarrow B \)はモノ射となる。

そこで、
\begin{eqnarray}
(f \circ g_1 \neq f \circ g_2) \lor (g_1 = g_2)
\end{eqnarray}
の部分は次のように変えることができる。
\begin{eqnarray}
(f \circ g_1 = f \circ g_2) \Rightarrow (g_1 = g_2)
\end{eqnarray}
これを利用して次のように定義することができる。

【モノ射の定義】
射\(f:A \rightarrow B \)が次の条件を満たす時、モノ射という。
条件:全ての対象\(C\)と全ての二つの射\(g_1: C \rightarrow A\)と\(g_2: C \rightarrow A\)に対して、
\begin{eqnarray}
f \circ g_1 = f \circ g_2
\end{eqnarray}
が成り立つとき、
\begin{eqnarray}
g_1 = g_2
\end{eqnarray}
である。[条件終了]

エピ射、モノ射とも対象の要素を使うことなく、関数の合成だけで定義しているところが大きな特徴である。集合でのそれと比べてどうであろうか。すっきりと見えるのではないだろうか。

追伸

エピ射もモノ射も全ての\(C\),\(g_1\),\(g_2\)に対してといっている。宇宙に存在するものすべてからだと、いつ証明が済むのかなと思ってします。これに代わるものを用意しておいて、この対象とこの射について調べてくれればいいよというものがあると助かる。そこで、これらを用意することにしよう。

モノ射:対象はシングルトンの空間、即ち、\(C=()\)とする。射は、\(A\)の各要素への写像、即ち、\(g_a:() \rightarrow a \in A\)とする。

エピ射:対象は2要素からなる空間、即ち、\(C=\{c_1,c_2\}\)とする。射は、\(B\)を2分し、一方を対象の一つに、他方を対象の残りに写像する、即ち、\(g_{B'}: (B' \subset B \rightarrow c_1 \lor B-B' \rightarrow c_2) \)とする。これの目的は次のようである。対象を2分し、別々の値に写像することで、全ての関数が異なるようになる。なお、\(C\)はブール値の集合\(\{true,false\}\)と考えてもよい。

射は閉じている:どれだけ合成しても大丈夫

2.4 合成

1)圏の構成要素

ここでは、これから説明する合成も含めて、圏を構成する要素についてまとめておこう。圏は次の構成要素から成り立つ。
1) 対象\(A,B,C,..\)を有する。
2) 射\(f,g,h,..\)を有する
3) 射はドメインとコドメインを有する。なお、ドメイン、コドメインは対象である。例えば\(f: A \rightarrow B, g: B \rightarrow C, h: A \rightarrow C,..\)
4) すべての対象に対して恒等射が存在する。\(1_A: A \rightarrow A, 1_B: B \rightarrow B, 1_C: A \rightarrow C,..\)
5) コドメインドメインが一致している射は合成でき、それも射となる。\(f: A \rightarrow B\)で \(g: B \rightarrow C\)ならば、\(g \circ f : A \rightarrow \)が存在する。

なお、圏は次の二つの法則を満たさなければならない。
① 結合律:合成が複数あるとき、結果は、その計算の順序によらず、どれから計算しても同じである。即ち、\( (f \circ g) \circ h = f \circ (g \circ h) \)である。
② 単位律:恒等射とある射の合成は、ある射となる。即ち、\(f \circ 1_A = f = 1_B \circ f\)である。この法則があるため、恒等写像であっても、恒等射とはならないものがある。これについては後で述べる。

2)合成

今回の話題は、圏を構成する要素の中で、合成と呼ばれるものである。合成は、射と射とを結びつけるものである。

射のところで話をしたように、射は、ドメインとコドメインを有する。一つの射のコドメインと他の一つの射のドメインが同じ対象である時、この二つの射は合成できる。

例えば、射\(f:A \rightarrow B,g:B \rightarrow C\)があった時、射\(g \circ f : A \rightarrow C\)が存在する。この時、\( \circ \)を合成という。

いくつか例を挙げよう。

① 対象\(A\)を実数とし、対象\(B\)を整数とし、対象\(C\)を自然数(0から始まる)とし、射\(f:A \rightarrow B\)は小数点以下を四捨五入する関数であるとし、射\(g:B \rightarrow C\)は絶対値をとる関数とする。この時、射\(g \circ f : A \rightarrow C\)は、中心(0)からの整数値による距離(負の値はとらない)を表す。

② 少し変わった合成を定義してみよう。射は自然数(1から始まる)としよう。対象は星とし、すべての射のドメインであり、コドメインであるとする (星なので、対象はあまり意味を持たないと考えてよい)。即ち、任意の自然数\(i\)に対して、\(i : \star \rightarrow \star \) である。
f:id:bitterharvest:20161011123105p:plain

合成は、\(i \circ j : \star \rightarrow \star \)となる。そこで、合成\( \circ \)を乗算とみなしてみよう。即ち、\(i \circ j = i \times j\)とする。この時、合成された射は、これらの射を掛けたものと同じになる。例えば、射3と5の合成は射15と同じである。即ち、\(5 \circ 3 = 15\)である。すべての射は、星自身への射(恒等写像)であるが、単位律を満たすのは1だけなので、これらの射の中で、1だけが恒等射となる。

③ 射を0から始まる自然数とし、合成を加算とみなすと、合成された射は、これらの射を加えたものとなる。例えば、射3と5の合成は射8と同じである。上記と同様の理由で、これらの射の中で、0だけが恒等射となる。

④ 射を\(+n\),\(n\)は自然数(0から始まる)としよう。また、対象を自然数(0から始まる)とし、すべての射のドメインであり、コドメインであるとする。(図参照)射\(+m\)と\(+n\)の合成は\(+(m+n)\)となる。即ち、\(+m \circ +n = +(m+n)\)である。また、恒等射は\(+0\)である。

⑤ 同様に射\(*n\)を考えることができる。この時は、\(n\)は 1から始まる自然数である。この時、恒等射は\(+1\)である。

3)Haskellで実現する。

それでは、Haskellで実現しよう。①から始めよう。0から始まる自然数を必要とするが、これは、前の記事で説明したものを利用する。

data Nat0 = Zero | Succ Nat0 deriving (Read, Eq, Ord, Show)

toNat0 :: Int -> Nat0
toNat0 m
  | m == 0 = Zero
  | m > 0  = Succ $ toNat0 (m - 1)
  | otherwise = error "Not Natural Number"

fromNat0 :: Nat0 -> Int
fromNat0 Zero   = 0
fromNat0 (Succ n) = 1 + fromNat0 n

それでは、四捨五入する関数\(round’\)を定義しよう。Haskellにも\(round\)という関数が用意されているが、この関数は、小数点の数が0.5の時、整数部が偶数のときは切り捨てを、奇数の時は切り上げをする。このため、四捨五入とはなっていないので、次のように定義する。

round' :: Double -> Int
round' m 
  | m >= 0 && m - fromIntegral (floor m) >= 0.5 = 1 + floor m
  | m >= 0 = floor m
  | m - fromIntegral (floor m) > 0.5 = 1 + floor m
  | otherwise = floor m

ここで、\(floor\)は小数点の切り捨てを行う。ただし、負の数の時は、整数部から1引いたものとなる。即ち、\(floor (-4.*)=-5\)である。では、\(round’\)を調べてみよう。

*Main> round' (-3.6)
-4
*Main> round' (-3.5)
-4
*Main> round' 3.6
4
*Main> round' 3.5
4
*Main> round' 3.4
3
*Main> round' 2.6
3
*Main> round' 2.5
3
*Main> round' 2.4
2
*Main> round' (-2.4)
-2
*Main> round' (-2.5)
-3
*Main> round' (-2.6)
-3
*Main> round' (-3.4)
-3
*Main> round' (-3.5)
-4
*Main> round' (-3.6)
-4

それでは、絶対値を求める関数を求めよう。整数を自然数に移すことに注意して、次のようにしよう。

abs' :: Int -> Nat0
abs' m
  | m >= 0 = toNat0 m
  | otherwise = toNat0 (-m)

大丈夫だと思うが、確認してみよう。

*Main> abs' 3
Succ (Succ (Succ Zero))
*Main> abs' 2
Succ (Succ Zero)
*Main> abs' 1
Succ Zero
*Main> abs' 0
Zero
*Main> abs' (-1)
Succ Zero
*Main> abs' (-2)
Succ (Succ Zero)
*Main> abs' (-3)
Succ (Succ (Succ Zero))

それでは、\(round’\)と\(abs’\)の合成関数\(distance\)を次のように定義しよう。

distance = abs' . round'

少し動かしてみよう。

*Main> distance 3.6
Succ (Succ (Succ (Succ Zero)))
*Main> distance 3.5
Succ (Succ (Succ (Succ Zero)))
*Main> distance 3.4
Succ (Succ (Succ Zero))
*Main> distance 2.6
Succ (Succ (Succ Zero))
*Main> distance 2.5
Succ (Succ (Succ Zero))
*Main> distance 2.4
Succ (Succ Zero)
*Main> distance (-2.4)
Succ (Succ Zero)
*Main> distance (-2.5)
Succ (Succ (Succ Zero))
*Main> distance (-2.6)
Succ (Succ (Succ Zero))
*Main> distance (-3.4)
Succ (Succ (Succ Zero))
*Main> distance (-3.5)
Succ (Succ (Succ (Succ Zero)))
*Main> distance (-3.6)
Succ (Succ (Succ (Succ Zero)))

このままだとわかりにくいので、\(fromNat0\)を用いて、整数に変換してみてみよう。

*Main> fromNat0 . distance $ 3.6
4
*Main> fromNat0 . distance $ 3.5
4
*Main> fromNat0 . distance $ 3.4
3
*Main> fromNat0 . distance $ 2.6
3
*Main> fromNat0 . distance $ 2.5
3
*Main> fromNat0 . distance $ 2.4
2
*Main> fromNat0 . distance $ -2.4
2
*Main> fromNat0 . distance $ -2.5
3
*Main> fromNat0 . distance $ -2.6
3
*Main> fromNat0 . distance $ -3.4
3
*Main> fromNat0 . distance $ -3.5
4
*Main> fromNat0 . distance $ -3.6
4

次に、②と③は脇において、④について考えよう。④では、対象を自然数としていたが、ここでは数に変えてみよう(数には整数、有理数無理数などが含まれる。分かりにくければ、整数と考えてもここでは問題ない)。このように変えると、Haskellで用意している\(+n\)を用いることができる。

(+3)という関数は次のようになっている。さらに、この関数に4を入力する。結果は、4に3くわえられた値7が出力される。

*Main> :t (+3)
(+3) :: Num a => a -> a
*Main> :t (+3) 4
(+3) 4 :: Num a => a
*Main> (+3) 4
7

それでは、合成関数を作ってみよう。+3と+4を合成した関数に、値4を入力してみる。

*Main> (+3) . (+4) $ 4
11

いくつもの関数を合成してみよう。

*Main> (+1) . (+2) . (+3) . (+4) . (+5) $ 4
19

同じことが⑤にも言える。

量子力学とHaskell - 目次 

1.身近な存在としての量子力学(1):MRI(磁気共鳴画像診断装置)(2):ヨーロッパコマドリ(3):進化論(4):光合成(5):光合成(続き)(6):光合成(続き)(7):ケット(8):ケットをHaskellで表現する(9):重ね合わせ(10):重ね合わせ(続き)、(11):ブラ(12):ブラをHaskellで記述する(13):ブラでの重ね合わせをHaskellで表現する(14):重ね合わせの内積
2.量子力学の初歩をHaskellで学ぶ(1):ブラとケット(2):重ね合わせ
3.量子力学の世界を垣間見る(1):粒子が相互に交換されるモデルの概略(2):粒子が相互に交換されるモデル:有限系(3):粒子が相互に交換されるモデル:周期系(4):ボーズ粒子とフェルミ粒子(5):離散系から連続系へ(6):ハミルトニアンの活用(7):シュレディンガー方程式

ハロウィン・パーティーで闊歩するバターナッツかぼちゃのポタージュ

月末はハロウィンだ。もともとは古代ケルト人の収穫を祝うお祭りのようだが、いつの間にか、日本にも定着したようで、テレビでは、連日、渋谷での警戒態勢を伝えている。

ハロウィンが近づくと、日本からルイジアナ州バトンルージュへ留学していた高校生が、仮装姿で間違って別の家に足を踏み入れたことで起きた悲しい事件がいつも思い出される。招待されている家だと思い込んでいた彼は、「フリーズ」と言われたのに「プリーズ」と聞き間違えたのだろう。銃を向けられていたにもかかわらず、歩き続けてしまい、射殺されてしまった。

この話は、もう4半世紀も前のことなので、ハロウィンにまつわる嫌な思い出は若いお母さんたちにはないようである。昨日出かけたショッピングセンターでは、幼稚園あるいは小学校の低学年の子供たちを伴った幾組みもの母と子が、狂騒的な声を張り上げながら、ハロウィン用の帽子を買い求めていた。

このような光景に出会って、ハロウィンが印象付けられたのだろう、食品売り場を物色している時に、カボチャ売り場で思わず足が止まった。ひときわ輝いているカボチャに出会ったからだ。他のカボチャとの差を見せつけようと、少しだけ高い場所に、しかも、竹で編んだざるの中に神々しく鎮座させられているものを発見した。

カボチャ売り場に置かれていなければ、瓢箪と間違えたであろう。形だけではなく、色も乾燥させた瓢箪とそっくりな薄い黄色だ。なんだろうと思いながら、また、どのようにして食べるのだろうと思って、あたりを見回してみたら、なんと、口上書きが添えられていた。
f:id:bitterharvest:20161025075158j:plain
名前は、バターナッツかぼちゃ。ピーナッツかぼちゃとも呼ばれるらしい。ピーナッツの殻と同じような形をしているので、このような名前がついたのだろう。しかし、ナッツというにはあまりにも大きすぎる。名はバターナッツよりも瓢箪の方がよさそうな気もする。しかし、自分の子供に可愛らしい名前を付けたがる人たちが増えた昨今では、余りにも工夫がない名前は嫌われるのであろう。そればかりではなく、味も何となく悪そうで、お客さんを惹きつけそうもない。カボチャの仲間たちに君臨するには、何とも愛らしい名前がふさわしいのであろう。

口上書きには、名前の紹介だけではなく、この後の身の振り方にも触れてあった。いたって簡単に記述されているのだが、ポタージュに変わりたいとのことだ。その儀式も一緒に書かれていた。それほど手間のかかるものではなさそうに見えたので、ハロウィンの時期の思い出として、バターナッツかぼちゃのポタージュに挑戦することとした。

買い求めたバターナッツかぼちゃはとてもいい形をしていて、コロコロとよく転がる。レジ袋に入れるときは、とても素早い動きで、テーブルから床へと逃げて行った。
f:id:bitterharvest:20161025071919j:plain
嫌がるバターナッツかぼちゃを強引に我が家に連れてきて、その美しい中身を拝見することにした。少し残酷だが、真っ二つに切り裂くことにした。かぼちゃの方は切られるのを嫌がって包丁を跳ね返すであろうから、軽く切り込みを入れて、横に滑らないように細心の注意を払いながら、垂直に力をかけて包丁に体重を乗せた。かぼちゃは、悲鳴を上げたが、その中身は、オレンジ色に近い鮮明な黄色であった。下の方は胎内なのだろう。未来の子孫が柔らかい綿でくるまれていた。
f:id:bitterharvest:20161025072001j:plain
かぼちゃの味を引き出すためには、玉ねぎをお供にするのがよい。優しく刻んで仲間に加える。
f:id:bitterharvest:20161025072040j:plain
かぼちゃと玉ねぎのペーストを作るので、かぼちゃの方は、皮をむいた後、
f:id:bitterharvest:20161025072108j:plain
1cm角程度に切り刻んだ。
f:id:bitterharvest:20161025072227j:plain
多数のコンポーネントに分かれてしまったかぼちゃと玉ねぎをお鍋に移し、さらに、美しくするために、化粧にマギーブイヨン2個と水500ccほど加えて、25分間煮込んだ。
f:id:bitterharvest:20161025072247j:plain
沸騰しているので、少し、冷ましてから、ミキサーにかけ、
f:id:bitterharvest:20161025072316j:plain
滑らかな肌触りのペーストへと変装させた。
f:id:bitterharvest:20161025072349j:plain
およそ半分ほどの牛乳(ペーストが1200ccほどあったので600ccの牛乳)を加え、塩と黒コショウで最後の化粧を整えた後、沸騰するまで、煮たてた。
f:id:bitterharvest:20161025081346j:plain

見事に仮装したので、オーストラリアから来た牛を伴って、ハロウィン・パーティーへ、いざ、出かけよう。
f:id:bitterharvest:20161025083013j:plain
ホテルのレストランのポタージュよりおいしかったと、歌舞いた姿が絶賛されたのは言うまでもない。

自己を表現する恒等射

2.3 恒等射

恒等射は自らを自らに写す射だ。これだけのことなので、今回の記事は簡単に済ますつもりでいた。

しかし、沢山ある射の中で恒等射を特別に圏の定義の中に含める必要がなぜあったのだろうか。恒等射は英語ではidentity(あるいはidentity morphism)である。

アイデンティティという用語は、自然科学で使っているときは軽やかに通り過ぎてしまうが、社会科学で用いるときは何とも重い感じがする。最近の英国のEUからの離脱や、米国でのトランプ氏の大統領候補選出などを観察していると、英国や米国の本来のアイデンティティがむき出しで表れてきたのではないかと感じさせられる。

世界が大きく変わってきているなと感じていたさなか、昨日、あるカルチャセンターで、本郷和人東大教授から鎌倉時代の話を伺った。その日の話題とは関係なかったのだが、冒頭でエマニュエル・トッドの家族型の説明を受けた。トッドは、ソ連邦の崩壊を予想し、実際にそのようになったことから注目され、今またEUの崩壊を予想している人口学の研究家である。

彼は、家族型を8つに分類し、それぞれの型と社会とがどのような関係にあるかを論じている。大きな分類としては、核家族、直系家族、共同体家族である。核家族とは一組のカップルが、直系家族とは二組(親と子の夫婦)あるいは三組(孫の夫婦まで一緒の場合)のカップルが、共同体とは多数(親族)のカップルが共に住むことが慣習となっているような家族制度である。

核家族は、さらに、絶対核家族(平等には無関心)と平等主義核家族に分かれ、前者にはイングランド、米国が含まれ、後者にはフランス北部(パリ)が含まれる。直系家族にはドイツや日本が含まれる。共同体家族は、さらに三つに分類されるが、外婚制共同体家族(いとこ婚は禁止)はロシア、旧ユーゴスラビア、中国が、内婚制共同体社会(いとこ婚は優先)には西アジア中央アジアが含まれる。

家族制度は、共同体社会→直系家族→核家族へと進化していくと考えがちだが、そうではないとエマニュエル・トッドは主張している。中心となっている地域が多くのカップルを含む家族制度に移行すると、その周辺地域には中心地域の家族制度が押し出され、さらに、その辺縁地域には周辺地域の家族制度が押し出されるとのことである。ユーラシア大陸には六つの中心地域があり、それらはロシア、中国などである。それらの国は共同体家族制度である。その周辺国であるドイツ、日本は直系家族制度であり、さらにその辺縁国であるイングランド核家族である。とても、面白い考え方で魅了される。

この研究成果は、『家族システムの起源』(2分冊)として出版されているので、機会を見つけて読んでみたいと思っている。
f:id:bitterharvest:20161015094017j:plain

エマニュエル・トッドは家族制度と基本的価値観やイデオロギーとの関係を見い出し、EU(具体的にはユーロ)の分裂、押し寄せる難民など、現代社会が抱える問題の結末を大胆に予測している。

家族制度は生活や考え方そして倫理観まで規定するであろうから、家族制度はそれぞれの人にアイデンティティを与えているといってよい。そして、人々がアイデンティティを大事にするとき、その結果として、現代社会が内包している危険な社会問題を引き起こすことになる。かといって、問題を解決するために、アイデンティティを捨てる訳にもいかないであろうから、今日の問題の解決は極めて難しい(今日のグローバリゼーションはアイデンティティの喪失を伴う。アイデンティティを失ったとき、人は何を夢見て生きていくのだろうか。経済合理性だけだとすると寂しすぎる)。

アイデンティティは、自分を赤裸々にするということともいえるが、圏論の恒等射の役割もここにある。圏論は、射だけでいろいろな性質を表現しようとするものだが、自身を表現しようとすると恒等射が必要になる。このため、射の中でも恒等射は特別な意味を有している。決して忘れてはならない射である。アイデンティティを必ず持ちましょうという射である。

恒等射は、一言でいうと、この記事の冒頭で述べたように、自らを自らに移す射である。従って、ドメイン(domain)とコドメイン(codomain)は同じ対象になるが、ドメインとコドメインで要素が入れ替わっているような射は恒等射ではない。
f:id:bitterharvest:20161015130942p:plain
あくまでも同じ要素への射でなくてはならない。
f:id:bitterharvest:20161015131002p:plain

恒等射をHaskellで実現してみよう。とても簡単で、全ての要素に対して同じ要素を返すようにすればよい。そこで、恒等射でのドメイン側の要素とコドメイン側の要素を対にしたリストで出力するプログラムを作成すると、次のようになる。

identity :: [a] -> [(a,a)]
identity a = [(x,x) | x <- a]

実行結果をいくつか示そう。

Prelude> :load "Identity.hs"
[1 of 1] Compiling Main             ( Identity.hs, interpreted )
Ok, modules loaded: Main.
*Main> identity [1..4]
[(1,1),(2,2),(3,3),(4,4)]
*Main> identity ['a'..'f']
[('a','a'),('b','b'),('c','c'),('d','d'),('e','e'),('f','f')]
*Main> identity ['a','g','f','k']
[('a','a'),('g','g'),('f','f'),('k','k')]

圏論は射から

2.2 射

圏論の中で一番重要な役割を担うのは射(arrowあるいはmorphism)である。射は対象を対象へと移す(写像という用語を使ったときは「写す」といったほうがよさそうだが、射は矢を射るという意味で用いているので「移す」という漢字を使う)。射は、他の分野では写像と呼ばれたり、関数と呼ばれたりする。しかし、圏論では、写像や関数の概念を抽象化して用いるので、射という特別な用語を用いる。

1)射とは

射\(f\)は、\(f:A \rightarrow B\)と記述される。\(A,B\)は対象で、\(A\)をドメイン(domain)、\(B\)はコドメイン(codomain)と呼ばれる。また、\(f(A)\)、即ち、ドメイン\(A\)が射\(f\)によって移される先をイメージ(Image)と呼ぶ。
f:id:bitterharvest:20161011120648p:plain

2)部分写像は射ではない

射の定義はかなり自由だが、それでもいくつか制限がある。その一つは、ドメインのいずれの要素も射によってコドメインのある要素に移されなければならない。しかし、反対に、コドメインの全ての要素が射のイメージになっている必要はない。射によって、全部であったり一部であったりしてよい。

関数では、このような関数を全関数(total function)と呼んでいる。しかし、我々が使っている写像の中には、全写像とはならないものがあることに注意しておく必要がある。例えば、お見合いパーティーをしたとしよう。パーティーに参加する人の情報をあらかじめ与えておいて、一番気に入っている人を当日会場の入り口で提出することにしよう。そして、相互に名前が一致した人達だけが、最初にお見合いをすることとする。この時、男性をドメインに、女性をコドメインにして、お見合いの相手を男性の方から女性の方に写像したとする。しかし、残念なことに、お見合いの相手がいない男性もいるであろう。このような場合には、ドメインのある要素に関数が定義されない。このようなものは部分写像(partial mapping)あるいは部分関数(partial function)と呼ばれるが、これは、集合と(全域)写像の圏の射ではない。
部分関数は、あるもの同士の関係を表す時によく現れる。例えば、データベースではこのようになっている場合が多い。
f:id:bitterharvest:20161011120742p:plain

また、関数は、一つの要素に複数の要素が対応することを許してはいないので、このようなものも圏論での射とはならない(ただし、関数の逆関数はこのようになることがある)。
f:id:bitterharvest:20161011120822p:plain

3)倍数を求める射

それでは、射の例をいくつか挙げてみよう。ドメインもコドメインも1で始まる自然数とする。射は2倍にする関数*2とする。この時の射は次のようになる。イメージは偶数となり、コドメインより小さい領域となる。
f:id:bitterharvest:20161011120928p:plain

それでは、上の射をHaskellで実現してみよう。Haskellでは射は関数という用語を使っているので、慣習に従って、Haskellのプログラムを説明しているときは、関数という用語を用いる。また、対象もデータ型という用語を用いる。

Haskell自然数のデータ型を用意していないので、自分で用意する必要がある。そこで、次のように定めよう。

data Nat1 = One | Succ Nat1 deriving (Read, Eq, Ord, Show)

とても短い定義だ。\(One\)は自然数1に対応する。\(Succ \ Nat1\) は自然数\(Nat1\)に続く自然数を表す。従って、\(Succ \ One\)は自然数1に続く自然数ということで2を表す。以下同様に、\(Succ \ (Succ \ One)\)は3である。
ところで、これではわかりにくいので、正の整数から対応する自然数を作り出す関数\(toNat1\)を用意しよう。これは以下のようにする。なお、記述\(toNat1 :: Int \rightarrow Nat1\)は次のことを意味する。関数\(toNat1\)はデータ型Intをデータ型\(Nat1\)へ移す。

toNat1 :: Int -> Nat1
toNat1 m
  | m == 1 = One
  | m > 0  = Succ $ toNat1 (m - 1)
  | otherwise = error "Not Natural Number"

また、逆に自然数から対応する整数を作り出す関数\(fromNat1\)も次のように用意しよう。

fromNat1 :: Nat1 -> Int
fromNat1 One   = 1
fromNat1 (Succ n) = 1 + fromNat1 n

それでは少し使ってみよう。自然数1から順番に求めてみよう。また、逆への変換も行ってみる。

Prelude> :load "Nat1.hs"
[1 of 1] Compiling Main             ( Nat1.hs, interpreted )
Ok, modules loaded: Main.
*Main> let v1 = toNat1 1
*Main> v1
One
*Main> let v2 = toNat1 2
*Main> v2
Succ One
*Main> let v3 = toNat1 3
*Main> v3
Succ (Succ One)
*Main> let v4 = toNat1 4
*Main> v4
Succ (Succ (Succ One))
*Main> let v8 = toNat1 8
*Main> v8
Succ (Succ (Succ (Succ (Succ (Succ (Succ One))))))
*Main> fromNat1 v1
1
*Main> fromNat1 v2
2
*Main> fromNat1 v8
8

それでは与えられた自然数の2倍を求める関数を求めてみよう。名前は、*2ではなく\(times2\)とする(*2はHaskellで既に使われているので、これとの混用を避ける)。2倍を求めるためには、与えられた数同士を加えればよい。そのためには、自然数を足し合わせる関数plusが必要になるので、これも一緒に定義することにしよう。次のようになる。

plus :: Nat1 -> Nat1 -> Nat1

m `plus` One = Succ m
m `plus` Succ n = Succ (m `plus` n)

times2 :: Nat1 -> Nat1
times2 m = m `plus` m 

関数\(plus\)は少し説明が必要である。自然数\(m\)と\(One\)を加えると、\(m\)に続く自然数\(Succ \ m\)となる。また、自然数\(m\)と\(Succ \ n\) (\(n\)に続く自然数)を加算すると、自然数\(m\)と\(n\)を加算して得られた自然数に続く自然数となる。これらを表したのが、上の定義である。なお、\(plus\)を中置関数として利用するときは、関数の前後に\(`\)をつけて、\(`plus`\)とする。さて、完成したので、実験してみよう。同じように自然数1から始めて、だんだんに数を大きくしてみる。

Prelude> :load "Nat1.hs"
[1 of 1] Compiling Main             ( Nat1.hs, interpreted )
Ok, modules loaded: Main.
*Main> let v1 = toNat1 1
*Main> let v1_2 = times2 v1
*Main> v1
One
*Main> v1_2
Succ One
*Main> let v2 = toNat1 2
*Main> let v2_2 = times2 v2
*Main> v2
Succ One
*Main> v2_2
Succ (Succ (Succ One))
*Main> let v3 = toNat1 3
*Main> let v3_2 = times2 v3
*Main> v3
Succ (Succ One)
*Main> v3_2
Succ (Succ (Succ (Succ (Succ One))))
*Main> let v7 = toNat1 7
*Main> let v7_2 = times2 v7
*Main> fromNat1 v7
7
*Main> fromNat1 v7_2
14

なお、上の定義で、関数\(times2\)は、\(times2 :: Nat1 \rightarrow Nat1\)という記述があったが、ここれは次のことを意味する。写像\(times2\)がデータ型\(Nat1\)をデータ型\(Nat1\)へ移す。これは圏論の用語を用いて説明すれば、射\(times2\)が対象\(Nat1\)を対象\(Nat1\)へ移すということになる。

4)剰余を求める射

それでは、ドメイン、コドメイン自然数だが、0始まりとする。そして、3で割った時の余りを求める関数を\(mod3\)とした時の射を求めてみよう。イメージは0,1,2となり、コドメインよりもずっと小さい領域である。
f:id:bitterharvest:20161011121030p:plain

それではHaskellで実現してみよう。0から始まる自然数\(Nat0\)を、先ほどの1から始まる自然数と同様に、定義してみよう。その時、一緒に、自然数と整数の間で変換する関数\(toNat0,fromNat0\)も定義しよう。以下のようになる。

data Nat0 = Zero | Succ Nat0 deriving (Read, Eq, Ord, Show)

toNat0 :: Int -> Nat0
toNat0 m
  | m == 0 = Zero
  | m > 0  = Succ $ toNat0 (m - 1)
  | otherwise = error "Not Natural Number"

fromNat0 :: Nat0 -> Int
fromNat0 Zero   = 0
fromNat0 (Succ n) = 1 + fromNat0 n

それでは、関数\(mod3\)を実現してみよう。次のようになる。

mod3 :: Nat0 -> Nat0

mod3 Zero                   = Zero
mod3 (Succ Zero)            = Succ Zero
mod3 (Succ (Succ Zero))     = Succ (Succ Zero)
mod3 (Succ (Succ (Succ m))) = mod3 m

使ってみよう。自然数0から始めて少しずつ数を大きくして調べてみる。

Prelude> :load "Nat0.hs"
[1 of 1] Compiling Main             ( Nat0.hs, interpreted )
Ok, modules loaded: Main.
*Main> let v0 = toNat0 0
*Main> v0
Zero
*Main> mod3 v0
Zero
*Main> let v1 = toNat0 1
*Main> v1
Succ Zero
*Main> mod3 v1
Succ Zero
*Main> let v2 = toNat0 2
*Main> v2
Succ (Succ Zero)
*Main> mod3 v2
Succ (Succ Zero)
*Main> let v3 = toNat0 3
*Main> v3
Succ (Succ (Succ Zero))
*Main> mod3 v3
Zero
*Main> let v4 = toNat0 4
*Main> v4
Succ (Succ (Succ (Succ Zero)))
*Main> mod3 v4
Succ Zero
*Main> let v15 = toNat0 15
*Main> v15
Succ (Succ (Succ (Succ (Succ (Succ (Succ (Succ (Succ (Succ (Succ (Succ (Succ (Succ (Succ Zero))))))))))))))
*Main> mod3 v15
Zero

5)複数の射

今度は、射が二つある場合を考えてみよう。対象を\(A={f,g,h},V={a,b,c,d}\)とする。また、射\(arc,trg\)はドメインを\(A\)とし、コドメインを\(V\)とし、下図のように移すとする。
f:id:bitterharvest:20161011121127p:plain
圏は抽象化されたものなので、それに対応させて様々な具体例を作ることができるが、上の射を実現したような実例を挙げることができるであろうか。下図はどうであろうか。
f:id:bitterharvest:20161011121141p:plain
上の図からわかるように、グラフは矢印をドメインとし、接続点をコドメインとし、矢印の元の方を接続点に移す射\(src\)と、矢印の先の方を接続点に移す射\(trg\)を用意することで圏となる。

5)可能な射をすべて求める

それでは、\(A={1,2,3}\)と\(B={a,b}\)の二つの対象があり、\(A\)をドメイン、\(B\)をコドメインとする射はいくつあるだろうか。ただし、同じ射は含めないとする。正解は下図のように\(2^3=8\)である。
f:id:bitterharvest:20161011122628p:plain

上の結果をHaskellで確かめてみよう。
ドメインとコドメインを次のデータ型\(Domain,Codomain\)で定義しよう。

data Domain = D1 | D2 | D3 deriving (Read, Show)
data Codomain = CA | CB deriving (Read, Show)

可能な射をリストとして出力されるようにしよう。即ち、リストの要素は一つの射に対応するものとする。また、それぞれの射は、ドメインの要素とコドメインの要素の対とする。それぞれの対では、ドメインの要素はその射によって移されるコドメインの要素に移されるものとする(リストが入れ子になっていることに注意)。これを実現したのが、下記のプログラムである。

morphism :: [((Domain, Codomain), (Domain, Codomain), (Domain, Codomain))]
morphism = [((D1,d1), (D2,d2), (D3,d3)) | d1 <- [CA, CB], d2 <- [CA, CB], d3 <- [CA, CB]]

この関数を実行してみよう。

Prelude> :load "Morphism.hs"
[1 of 1] Compiling Main             ( Morphism.hs, interpreted )
Ok, modules loaded: Main.
*Main> morphism
[((D1,CA),(D2,CA),(D3,CA)),((D1,CA),(D2,CA),(D3,CB)),((D1,CA),(D2,CB),(D3,CA)),((D1,CA),(D2,CB),(D3,CB)),((D1,CB),(D2,CA),(D3,CA)),((D1,CB),(D2,CA),(D3,CB)),((D1,CB),(D2,CB),(D3,CA)),((D1,CB),(D2,CB),(D3,CB))]

上記の結果で、リストの要素が一つの射を表しているので、可能な射は、
\(( (D1,CA),(D2,CA),(D3,CA) )\)
\(( (D1,CA),(D2,CA),(D3,CB) )\)
\(( (D1,CA),(D2,CB),(D3,CA) )\)
・・・
の8個である。これは、先ほど示したものと一致する。
また、上記の射は、ドメインの要素とそれによって移されるコドメインの要素を示している。最初の射では、\(D1\)を\(CA\)に、\(D2\)を\(CA\)に、\(D3\)を\(CA\)に移す。

6)個数\(m\)のドメインから個数\(n\)のコドメインへの射

一般にドメインの要素数が\(m\)でコドメインの要素数が\(n\)のとき、異なる射の数は\(n^m\)となる。

いくつか確認してみよう。コドメインの要素数が1の時は、異なる射の数は1個である。例えば、ドメイン自然数とすると、射は次のようになる。
f:id:bitterharvest:20161011122747p:plain

ドメインの要素数が0の時は、射は存在しない。

それでは、ドメインの要素数を1としよう。この時は異なる射の数はコドメインの要素の数となる。例えば、コドメイン自然数であったとすると次のようになる(図では\(one,two,three\)まで書いたが、そのあと無限に続く)。
f:id:bitterharvest:20161011122850p:plain

それでは、ドメインの要素数が0の時は、どうであろうか。式からは1となる。そこで、ドメイン空集合の時は、コドメイン全体に移す射が一つあることにする。
f:id:bitterharvest:20161011122919p:plain

それでは、Haskellドメインの要素数が\(m\)でコドメインのそれが\(n\)の時の関数をすべて求めてみよう。

このプログラムは相当に手ごわいので、少しずつ部品を用意しながら、完成に導こう。今、ドメインの要素数が3であり、要素は順番に\(a1,a2,a3\)となっていったとしよう。これをリストで表すと\([a1,a2,a3]\)となる。一方、コドメインの要素数は2であり、順番に\(b1,b2\)であったとする。ドメインの要素に対してコドメインの要素を対応させることになるが、ドメインの右の方から処理していくことにする。

最初に、行うのは、\(a3\)となる。そこで、\(a3\)を\(b1,b2\)で置き換えたリストを\([[b1],[b2]]\)とする。次に\(a2\)を置き換えて、それを先ほど得たリストの頭にくっつけることにしよう。そうすると、\([[b1,b1],[b1,b1],[b2,b1],[b2,b2]]\)をえる。さらに、\(a1\)についても同じことを行えば、ドメインからコドメインに移す異なる射をすべて得ることができる。

そこで、置き換えられたリストから次の要素も置き換えたリストを作る関数\(addRep\_\)を定義しよう。

addRep_ :: [b] -> [[b]] -> [[b]]
addRep_ [] _ = []
addRep_ _ [] = []
addRep_ (y:ys) (l:ls) = (y:l) : addRep_ [y] ls ++ addRep_ ys (l:ls)

確認してみよう。

*MorphismA> addRep_ [1,2] [[1],[2]]
[[1,1],[1,2],[2,1],[2,2]]

うまく動いているようである。それでは、この操作を左端から右端まで再帰的に順番に行う関数\(morphism\_\)を定義しよう。

morphism_ :: [a] -> [b] -> [[b]]
morphism_ _ [] = []
morphism_ [] ys = [ys]
morphism_ (x:[]) (y:ys) = [y] : morphism_ (x:[]) ys
morphism_ (x:xs) ys = addRep_ ys $ morphism_ xs ys

上の関数で、左端についての処理を行うのが、以下の部分である。

morphism_ (x:[]) (y:ys) = [y] : morphism_ (x:[]) ys

途中の処理は次の部分である。

morphism_ (x:xs) ys = addRep_ ys $ morphism_ xs ys

すでに置き換えの済んでいるリスト\(morphism\_ \ xs \ ys\)に新しい置き換え\(addRep\_ \ ys\)を付け加えている。

正しく動いているかどうか確認してみよう。

*MorphismA>  morphism_ [1,2,3] [1,2]
[[1,1,1],[1,1,2],[1,2,1],[1,2,2],[2,1,1],[2,1,2],[2,2,1],[2,2,2]]
*MorphismA>  morphism_ ['a', 'b', 'c'] [1,2]
[[1,1,1],[1,1,2],[1,2,1],[1,2,2],[2,1,1],[2,1,2],[2,2,1],[2,2,2]]

うまく動いているようだ。

また、プログラムで

morphism_ [] ys = [ys]

は、空集合に対する射である。次のようになる。

*MorphismA>  morphism_ [] [1,2]
[[1,2]]

全体への射となる。

さて、部品が完成したので、これはモジュール\(MorphingA\)としておこう。

module MorphismA (morphism_) where

morphism_ :: [a] -> [b] -> [[b]]
morphism_ _ [] = []
morphism_ [] ys = [ys]
morphism_ (x:[]) (y:ys) = [y] : morphism_ (x:[]) ys
morphism_ (x:xs) ys = addRep_ ys $ morphism_ xs ys

addRep_ :: [b] -> [[b]] -> [[b]]
addRep_ [] _ = []
addRep_ _ [] = []
addRep_ (y:ys) (l:ls) = (y:l) : addRep_ [y] ls ++ addRep_ ys (l:ls)

要素の数が与えられた時は、配列を自動的に作り出して、\(morphism\_\)を呼び出して、可能な関数をリストの列で与える関数\(morphism\)を次のように定義する。

morphism :: (Num b, Num a, Enum b, Enum a) => a -> b -> [[b]]
morphism m n = morphism_ [1..m] [1..n]

これの動作を確認しよう。

*Main>  morphism 3 2
[[1,1,1],[1,1,2],[1,2,1],[1,2,2],[2,1,1],[2,1,2],[2,2,1],[2,2,2]]

うまく動いているようである。

上記では、ドメインからコドメインへの射は配列、例えば[1,1,1]、で与えられているが、これをタプル、即ち(1,1,1)、で表すことにしよう。

これは、コドメインの要素数ごとに関数を用意する。要素数が0に対しては\(morphism0、1\)に対しては\(morphism1、2\)に対しては\(morphism2\)のように定義しよう。次のようになる。

morphism0 :: (Num a, Enum a) => a -> [[a]]
morphism0 n = morphism 0 n

morphism1 :: (Num a, Enum a) => a -> [a]
morphism1 n = toPair1 $ morphism_ [1..1] [1..n]
  where 
    toPair1 [] = []
    toPair1 (f:fs) = (f !! 0) : toPair1 fs 

morphism2 :: (Num a, Enum a) => a -> [(a, a)]
morphism2 n = toPair2 $ morphism_ [1..2] [1..n]
  where 
    toPair2 [] = []
    toPair2 (f:fs) = (f !! 0, f !! 1) : toPair2 fs 

morphism3 :: (Num a, Enum a) => a -> [(a, a, a)]
morphism3 n = toPair3 $ morphism_ [1..3] [1..n]
  where 
    toPair3 [] = []
    toPair3 (f:fs) = (f !! 0, f !! 1, f !! 2) : toPair3 fs 

morphism4 :: (Num a, Enum a) => a -> [(a, a, a, a)]
morphism4 n = toPair4 $ morphism_ [1..4] [1..n]
  where 
    toPair4 [] = []
    toPair4 (f:fs) = (f !! 0, f !! 1, f !! 2, f !! 3) : toPair4 fs 

morphism5 :: (Num a, Enum a) => a -> [(a, a, a, a, a)]
morphism5 n = toPair5 $ morphism_ [1..5] [1..n]
  where 
    toPair5 [] = []
    toPair5 (f:fs) = (f !! 0, f !! 1, f !! 2, f !! 3, f !! 4) : toPair5 fs 

morphism6 :: (Num a, Enum a) => a -> [(a, a, a, a, a, a)]
morphism6 n = toPair6 $ morphism_ [1..6] [1..n]
  where 
    toPair6 [] = []
    toPair6 (f:fs) = (f !! 0, f !! 1, f !! 2, f !! 3, f !! 4, f !! 5) : toPair6 fs

分かりやすいところから、実行してみよう。ドメインとコドメインとも要素数が2の場合は次のようになる。

*Main>  morphism2 2
[(1,1),(1,2),(2,1),(2,2)]

これは次のように解釈される。射は4個ある。それらは、(1,1),(1,2),(2,1),(2,2)である。最初の射(1,1)は、ドメインの最初の要素をコドメインの最初の要素に、そして、ドメインの最後の要素をコドメインの最初の要素に移す。ここで、ドメインの要素の順番は適当でよい。コドメインについても同じである。

ドメインの要素数が3になるとどうなるであろうか。

*Main>  morphism3 2
[(1,1,1),(1,1,2),(1,2,1),(1,2,2),(2,1,1),(2,1,2),(2,2,1),(2,2,2)]

4では、

*Main>  morphism4 2
[(1,1,1,1),(1,1,1,2),(1,1,2,1),(1,1,2,2),(1,2,1,1),(1,2,1,2),(1,2,2,1),(1,2,2,2),(2,1,1,1),(2,1,1,2),(2,1,2,1),(2,1,2,2),(2,2,1,1),(2,2,1,2),(2,2,2,1),(2,2,2,2)]

だいぶ関数が多くなってきた。関数の数を数えてみよう。今得た結果に\(length\)を施すと求めることができる。

*Main> length $ morphism4 2
16

素数が5と6の時についても求めてみよう。

*Main> length $ morphism4 2
16
*Main> length $ morphism5 2
32
*Main> length $ morphism6 2
64

それでは、ドメインの要素数が1の時の射を求めてみよう。

*Main>  morphism1 2
[1,2]

本当は、[(1),(2)]と表示されることを望んだのだが、()がなくても同じということで、省かれている。
ドメインの要素数が1の時の射を求めてみよう。

*Main>  morphism0 2
[[1,2]]
*Main> length $ morphism0 2
1

全体に移され、射の総数は1である。

それでは、コドメインの要素数を変えてみよう。1個の場合はどうであろうか。

*Main>  morphism0 2
*Main> morphism6 1
[(1,1,1,1,1,1)]
*Main> morphism5 1
[(1,1,1,1,1)]
*Main> morphism4 1
[(1,1,1,1)]
*Main> morphism2 1
[(1,1)]
*Main> morphism1 1
[1]
*Main> morphism0 1
[[1]]

射の数はどれも1であることが確認できる。

それでは0の場合はどうであろうか。

*Main> morphism6 0
[]
*Main> morphism5 0
[]
*Main> morphism4 0
[]
*Main> morphism3 0
[]
*Main> morphism2 0
[]
*Main> morphism1 0
[]
*Main> morphism0 0
[]

個数は0だ。

Haskellでは空集合をデータ型Data.Voidで定義している。その中に、\(absurd\)という関数があって、\(Void\)を任意の型に移している。

Prelude> import Data.Void
Prelude Data.Void> :i Void
data Void 	-- Defined in ‘Data.Void’
instance [safe] Eq Void -- Defined in ‘Data.Void’
instance [safe] Ord Void -- Defined in ・eData.Void’
instance [safe] Read Void -- Defined in ‘Data.Void’
instance [safe] Show Void -- Defined in ‘Data.Void’
Prelude Data.Void> :t absurd
absurd :: Void -> a

論理学では\(Void\)は偽の値を持つ。\(Absurd\)の関数を\(if \ Void \ then \ a\)とみなすならば、\(Void\)が偽であることから、\(a\)は何であったもよいことになる。即ち、任意の型でよいことになる。このことを表しているのが、\(absurd\)だが、使う機会はなさそうである。

7)星の上の対象

圏論を勉強しているときに、下記の図を見てびっくりした。驚愕したという方が適切かもしれない。
f:id:bitterharvest:20161011123105p:plain
この図を見るまでは自然数は対象にしかならないと思い込んでいたが、なんとここでは射になっている。全く異質な世界を見せられた。射であるからには、自然数はあるものをあるものに移さなければならない。図では星を星に移していた。明らかに対象なのだが、なぜ、星で表しているのであろうか。

圏論の入門書を読むと、最初の方に対象の詳細を知ることなしに、論じることができる数学の体系を考えようという趣旨のことが書かれている。対象は集合やグラフなど様々な構造を持っている。その構造に重点を置いて論じると、集合論グラフ理論位相空間論などの様々な数学の分野に落ち込んでしまう。そこで、構造を気にしないで論じてみようというのが圏論だ。

しかし、圏論の入門書を読んでいると、集合から初めてグラフへと説明が移行していくので、圏論の本来の趣旨をすっかり忘れて、対象の構造にどっぷりしたって考える癖がついてしまう。そのような時に出会ったのが、上の図である。

星が対象の本体の趣旨を思い出させてくれた。星が表しているのは対象なのでその中身は気にしないでねというメッセージを送ってくれている。とても高尚な表現である。

この図が持つ正確な意味は、射と射の合成を論じてからでないと、説明できない。従って、ここでは問題の提起だけにして、楽しい話はしばらく後にしよう。

境を明確にする対象

2.圏の定義

圏論の核となる圏の定義はとても単純である。抽象化は、細かいことを切り捨て、本質となている部分を取り出していく作業である。別の言い方をすると、数学の様々な分野での固有な部分を切り捨て、それらに共通する部分を抜きだすことである。圏論は抽象化の結果なので、その定義が単純なのは当たり前だとも思えるが、抽象化された後に得られた概念が大切なので、それらを少し詳しく説明する。

2.1 対象

数学で一番重要な概念は、等しいという概念だと思う。等しいという概念を最初に理解するのは、おそらく、三角形の合同だろう。二つの三角形を重ね合わせることができるなら、二つの三角形は等しいと学ぶ。しかし、これでは、数学的な厳密性を欠くので、一片とその両端の角がそれぞれ等しい時、二つの三角形は合同であるなどというように定義される。

三角形の合同は、それぞれの三角形の細かな違いを無視している。もしかすると、それぞれの三角形には、模様がついていて、それぞれで異なっているかもしれない。あるいは、三角形を作り出している素材が異なっているかもしれない。しかし、このような細部での違いを無視して、別の言葉を使うならば、抽象化をして、三角形の合同を定義している。

同じ性質を有するものを型という。型は自由に決めることができる。相互に合同である三角形を下図のように一つの型と決めてもよい。この型には無数の三角形が存在することになる(但し、全てが相互に合同である)。
f:id:bitterharvest:20161003095854p:plain

合同を学んだあと、相似な三角形を学ぶ。その後で、射影幾何学を学んだ人もいるだろう。ここでは、相似な四角形を定義できる。図のように一つの光源から光を四角形を当てると後ろに張った暗幕に影ができる。同一の影を作るような四角形は射影的合同と呼ばれるが、これに対しても型を作ることができる。
f:id:bitterharvest:20161004090228p:plain

もっと抽象化を行い、一つだけ穴が空いているものという型を作ることもできる(穴の定義が曖昧だが、例えば、これはホモトピー基本群が無限巡回群になる物体で、その次元を問わない)。
f:id:bitterharvest:20161003103336p:plain

型は、文学的に記述すればミウチとそうでない者との間に境目をつけることだ。少し前から読んでいた、永井路子著『美貌の女帝』は、蘇我氏の血筋を引くものを守り、新興勢力として伸びつつある藤原氏を排除することを政権の課題とする氷高皇女(ひたかひめみこ、後の元正天皇)の苦渋に満ちた人生を伝えている。実世界の中で、ミウチとそうでない者との間に境を設けようとすると、とても厳しい事態となるが、数学の世界では合理的に割り切って、境をつけることにしよう。

型は、圏論では対象(an object)という。対象はある性質を共有しているもの(数学的に等しいもの)である。対象には、いくつかの要素が含まれる(まったく含まれない場合もあるがこの話は後でする)。その数は、有限であるかもしれないし、無限であるかもしれない。先ほどの幾何学的な例に加えて、いくつかを紹介しよう。なお、対象を定義するときは、どのような性質を共有しているのかをよく考えながら定義することが大切である。曖昧に定義すると後でとんでもないしっぺ返しを食らう。

よく利用するのは、有限な単純な値の集まりであると思う。例を挙げてみよう。なお、ここでは、集合と言う言葉ではなく、集まりという言葉を使う(集合と言う用語を用いると、いわゆるラッセルのパラドックスに陥るので、ここではそれを避けるために集まりとする)。

1)単純な値の集まり(有限な個数)

・月={1月,2月,..,12月}
・曜日={日,月,火,水,木,金,土}
・苗字={青井,安藤,..,渡辺}

個数が有限でない場合、即ち無限の時は、数え上げられる場合とそうでない場合とに分けられる。まず数え上げられる場合を考えてみよう。

2)単純な値の集まり(無限だが数え上げできる個数)

自然数={1,2,3,..} (0を含める場合もある)
・整数={..,-1,0,1..}

数え上げられない場合は次のようである。

3)単純な値の集まり(数えきれないほどの無限な個数)

・半径1の円内の点
・実数
複素数

単純な値を組み合わせて、構造的な値を有するものもある。

4)構造的な値の集まり

・三角形
・正多角形
・合同な三角形
ホモトピー基本群が無限巡回群になる物体
・野菜
・車

Haskellでは対象はデータ型として表す。データ型は自由に定義することができるが、使用頻度の高いものはシステムであらかじめ用意している。それらは、

データ型説明
Int整数(有界\(-2^{63} \sim 2^{63}-1\))
Integer整数(無限)
Float単精度浮動小数点数
Double倍精度浮動小数点数
Bool真理値
Char文字(Unicode)

また、文字列\(String\)はデータ型ではないが、頻繁に用いるので、同じような感じで使えるようになっている。

また、新しいデータ型を用意することもできる。その時は、\(data\)を用いる。例えば、曜日のデータ型\(Day\)は次のようになる(なお、データ型の名前の、この場合\(Day\)の、先頭は必ず大文字である。また、データ型の名前は型コンストラクタと呼ばれる)。

data Day = Sunday | Monday | Tuesday | Wednesday | Thursday | Friday | Saturday deriving (Show, Read)

これをファイルDay.hsに書き込んでおこう。そして、このプログラムを動かしてみよう。Haskellは今年の5月21日に版の改正があり、最新のバージョンはGHC8.0.1である。Windows用には、WinGHCiを用いると便利である。動かした結果は次の通りである。
f:id:bitterharvest:20161003152753p:plain

ファイルを読み込んだ後で、データ型\(Day\)が正しく処理されているかを見るために、検査(inspection)してみる。これにはコマンド\(:i\)を用いる。
正しく定義されているので、次は変数\(a\)の値を月曜日にしてみる。\(a\)の型(\(type\))を調べてみる。これには\(:t\)を用いる。ちゃんとなっている。最後に\(a\)の値を求めると\(Monday\)と出力される。

なお、データ型\(Day\)を定義するときに\(deriving (Show,Read)\)とした。これについては射のところで詳しく説明するが、ここでは、読込み、かつ、出力できるようにしたと理解して欲しい。

図形を扱えるようにするために、ユークリッド座標上の点に関するデータ型\(Point\)を用意しよう。

data Point = Point (Double, Double) deriving (Show, Read)

この定義をファイルPoint.hsに書き込み、これを読みだして使ってみよう。点\(p1=(2.4,4.9)\)と\(p2=(3,4)\)を作成してみよう。

Prelude> :load "Point.hs"
[1 of 1] Compiling Main             ( Point.hs, interpreted )
Ok, modules loaded: Main.
*Main> :i Point
data Point = Point (Double, Double) 	-- Defined at Point.hs:1:1
instance [safe] Read Point -- Defined at Point.hs:1:53
instance [safe] Show Point -- Defined at Point.hs:1:47
*Main> let p1 = Point (2.4, 4.9)
*Main> p1
Point (2.4,4.9)
*Main> :t p1
p1 :: Point
*Main> let p2 = Point (3, 4)
*Main> p2
Point (3.0,4.0)
*Main> :t p2
p2 :: Point


次はこれを用いて三角形のデータ型\(Triangle\)を定義してみよう。定義に当たっては重心と2頂点を入力することにしよう。定義は次のようになる。

data Triangle = Triangle Point Point Point deriving (Show, Read) -- the gravity center, two vertexes

この定義をファイルPoint.hsに追加して使ってみよう。
以下では、重心が\((0,0)\)、頂点が\((0,1), (\sqrt{3}/2,-1/2), (-\sqrt{3}/2,-1/2)\)の三角形\(t1\)を作ろう。作成に当たっては重心と最初の2頂点を用いる。
f:id:bitterharvest:20161004180048p:plain

Prelude> :load "Point.hs"
[1 of 1] Compiling Main             ( Point.hs, interpreted )
Ok, modules loaded: Main.
*Main> :i Triangle
data Triangle = Triangle Point Point Point
  	-- Defined at Point.hs:3:1
instance [safe] Read Triangle -- Defined at Point.hs:3:60
instance [safe] Show Triangle -- Defined at Point.hs:3:54
*Main> let t1 = Triangle (Point (0,0)) (Point (0,1)) (Point (sqrt 3, (-0.5)))
*Main> t1
Triangle (Point (0.0,0.0)) (Point (0.0,1.0)) (Point (1.7320508075688772,-0.5))
*Main> :t t1
t1 :: Triangle

さらに正多角形のデータ型\(RegularPolygon\)を定義してみよう。定義に当たっては辺の数と重心と頂点の一つを入力することにしよう。定義は次のようになる。

data RegularPolygon = RP Int Point Point deriving (Show, Read)  -- edges, the gravity center, a vertex point

\(data\)の次にある単語はデータ型に与えられた名前で、前述したように、型コンストラクタと呼ぶ。これまで、\(Point, Triangle\)を定義してきた。ところで、等号の左側にも、これまでは型コンストラクタと同じ名前を用いてきた。しかし、等号の左側はデータコンストラクタと呼ばれ、型コンストラクタと同じ名前である必要はない。そこで、今回は、型コンストラクタに\(RegularPolygon\)を、データコンストラクタに\(PR\)という名前を用いることにした。

この定義をファイルPoint.hsに追加して使ってみよう。
以下では、重心が\((2,2)\)、頂点の一つが\((2,4)\)の五角形\(p1\)を作ろう。
f:id:bitterharvest:20161004192942p:plain

Prelude> :load "Point.hs"
[1 of 1] Compiling Main             ( Point.hs, interpreted )
Ok, modules loaded: Main.
*Main> :i RegularPolygon
data RegularPolygon = RP Int Point Point
  	-- Defined at Point.hs:5:1
instance [safe] Read RegularPolygon -- Defined at Point.hs:5:58
instance [safe] Show RegularPolygon -- Defined at Point.hs:5:52
*Main> let p1 = RP 5 (Point (2,2)) (Point (2,4)) 
*Main> p1
RP 5 (Point (2.0,2.0)) (Point (2.0,4.0))
*Main> :t p1
p1 :: RegularPolygon

図形につかれたので、車についてのデータ型を定義てみよう。メーカー名(\(company\))と車種(\(style\))と製造年(\(production\))を属性に持たせることにしよう。これには、レコード構文を使うと便利である。レコードはいくつかのフィールドからなり、各フィールドは型を指定できるようになっている。そこで、\(company\)と\(style\)を文字列で、\(production\)を整数の型とし、次のように\(Car\)を定義する。

data Car = Car { company :: String , style :: String , production :: Integer} deriving (Show, Read)

この定義をファイルCar.hsに追加して使ってみよう。
ここ10年使用している車\(c1\)を得てみよう。若者用に売り出されたのだが、現在は、残念ながら製造されていない。
f:id:bitterharvest:20161004202405j:plain

Prelude> :load "Car.hs"
[1 of 1] Compiling Main             ( Car.hs, interpreted )
Ok, modules loaded: Main.
*Main> :i Car
data Car
  = Car {company :: String, style :: String, production :: Integer}
  	-- Defined at Car.hs:1:1
instance [safe] Read Car -- Defined at Car.hs:1:95
instance [safe] Show Car -- Defined at Car.hs:1:89
*Main> let c1 = Car {company="Honda", style="Air Wave", production=2006}
*Main> c1
Car {company = "Honda", style = "Air Wave", production = 2006}
*Main> :t c1
c1 :: Car

動作を確認できたと思う。野菜についても同じように定義できるが、ここは、読者の方で考えてみよう。

これまで、型を作るためのいくつかの方法を述べたが、代数的な計算を利用してデータ型を作りたい場合には、代数的データ型がある。量子力学の世界をHaskellで表現しようとするとき、最も基本的な記述であるブラを代数的データ型を用いて定義した。再帰的な構造を持つ型を定義しようとするとき、代数的データ型は便利である。

最後にまとめです。圏論での対象は型である。型はある性質を表すものの集まりである。Haskellは対象をデータ型として表す。

なぜHaskell、なぜ圏論なのか

1.初めに

これまで、さまざまな言語でプログラミングしてきたが、一番満足しているのはHaskellである。なぜという問いに一言で答えるならば、バグが入りにくい、あるいは、プログラムが信用できるということだろう。
f:id:bitterharvest:20161002140825p:plain
この記事の前に、量子力学の世界をHaskellで構築することを試みた。連続系についてはまだ説明の途中であるが、その基本となる離散系については完成している。量子力学は物理の世界でも難しい分野の一つだ。概念的に複雑な世界を記述しようとすると、とても、抽象度の高いプログラミング言語を必要とする。

現在のHaskellは、圏論(category theory)という数学を応用したプログラミング言語である。他の学問と比較すると、数学は抽象度が高い。圏論は、その中で、最も抽象度が高い分野の一つである(圏論よりも抽象度が高いのは最近話題になることが多いホモトピー型理論だ。この理論がプログラミングの世界で使われるようになると、プログラムの証明はもっと簡単になると予想している)。

Haskellは抽象度がとても高いので、応用分野に適した枠組みを提供するのにとても適している。量子力学のような高度な物理概念も、Haskellを用いることで、量子力学用のフレームワークを割合と簡単に提供することができる。とはいっても、量子力学圏論Haskellの3つを理解していなければならないのでハードルは高い。それでも、それぞれに精通していれば合理的な時間の中で完成させることができる。しかし、他の言語、C++Javaでと言われると二の足を踏む。取り掛かる気にもならない。本格的な作業を始める前に準備するものが多すぎて、時間もかかりそうだし、良いものができる保証もない。

ブログを見ていると、Haskellに挫折した人が多い。恐らくは、その基本となっている概念、即ち、圏論を理解していないためだと思われるので、圏論について、もう一度説明してみたいと思う。

Bartosz Milewskiという方を知っているだろうか。 Haskell圏論に関連するブログを検索すると時々見かける名前だ。

ブログのプロファイルによれば、ポーランドで教育を受け、理論物理で博士の学位を授与されている。その後、欧米でポスドクを経験し、マイクロソフト検索エンジンの開発を行ったそうだ。会社の方がインターネットビジネスに消極的であったこともあり、うまくいかなかったそうである。その後、Reliable Softwareという会社を立ち上げ、現在は、ワシントン大学情報科学の大学院に在籍しているとのことである。写真を見る限りでは、ある程度、歳が行っているように見えるが

彼が、シアトルで、プログラマーのために、10週間のコースを開催中である。講義名は、Category Theory for Programmersである。講義の内容はYouTubeで紹介されている。また、書籍での出版も望んでいるようで、途中までの原稿もブログに掲載されている。

さて、Haskellを皆さんに勧める理由をもう少し詳しく説明しよう。

プログラミングを学んで最初に戸惑う記述は
\begin{eqnarray}
a=a+1
\end{eqnarray}
だろうと思う。方程式の記述に慣れてきた身には
\begin{eqnarray}
3x=2x+1
\end{eqnarray}
という記述は抵抗なく受け入れることができる。左辺の\(3x\)と右辺の\(2x+1\)は等しいと訴えている。この時、\(x=1\)だと理解できる。しかし、同じように考えたとき、\(a=a+1\)を満たす\(a\)はないよということになる。

プログラミングしていて出会うこの悩ましい記述を理解するためには、コンピュータのことをもう少し詳しく知っている必要がある。コンピュータにはプロセッサとメモリがあって、プロセッサは計算し、メモリはデータを記憶する(正確に言うと、プログラムもここに記憶される)。メモリには、番地がついていて、データを取り出す時も、データを格納するときも、この番地を用いる。プロセッサはコンビニエンスストア―のキャッシュレジスターと同じ役割をする。プロセッサには、レジスターがあり、データを一時的に記憶することができる。また、レジスターのデータに対して四則演算を行うことができる。その時、足す数、引く数、掛ける数あるいは割る数などは、メモリーより読みだす。メモリから読みだされたデータを使って、レジスタにあるデータに対して指定された演算を行う。その結果は番地を指定してメモリに記憶することができる。

\(a=a+1\)は、\(a\)という名前で呼ばれている番地から、そこに格納されているデータを取り出し、それをプロセッサのレジスタに移動する。次にレジスタの内容に\(1\)を加える(\(1\)という値はメモリのどこかにしまわれていて、ここから取り出して用いる)。ここまでの手続きが、右辺で示されている。右辺の処理が終了すると、左辺の処理に入る。レジスタの内容を\(a\)という名前で呼べれている番地に記憶する。

プログラミングで用いる記述は、通常は、数学で用いるものと異なっていることが多い。このため、コンピュータの構造やコンピュータの内部での処理を素直に記述するアセンブリ言語を学ばないと、初学者は独特の記述をなかなか理解することができない。

\(a\)は変数と呼ばれるが、変数の値が変わることが許されているものをミュータブル(mutable)という(そうでないものをイミュータブル(immutable)という)。多くのプログラミング言語は、変数がメモリの格納場所を表しているために、ミュータブルである。

しかし、コンピュータが広く使われるようになるにしたがって、アセンブリ言語ではプログラミングしにくいので、いわゆる高級言語と呼ばれるものが開発された。プログラミングの世界でも抽象化が行われるようになる。抽象化とは、異なっていると思われる複数の世界の中から、共通する性質を見つけ出す作業である。抽象化することによって、それまで別々のプログラムとして実装されていたものが、共通する部分は同じプログラムで実現できるようになり、開発費や信頼性の向上につながる。

各種の高級言語が出る中で、オブジェクト指向の概念は大きな画期であった。そこでは、プログラムはオブジェクトの集まりで、オブジェクト間でメッセージを受け渡すことで処理が進行する。オブジェクトはメッセージを受け取ると、その処理を行う。その処理は外部に対しては隠蔽されている。このため、もっと良い方法が見つかれば、処理の中身を自由に変更できるという素晴らしい利点を有している。オブジェクト指向の概念は、技巧的なプログラミングの世界を科学的・工学的にするという一翼を担った。僕自身もとても良いプログラミングスタイルだなと考え、情報科学部を設立したときに、1年生からJavaを学べるようにした。

しかし、変数はミュータブルであったために、並行処理が盛んになるに従ってその問題点が浮き彫りになってきた。オブジェクト指向では、オブジェクトは状態を有する。そして、状態はミュータブルである。状態の変更はオブジェクト内部の処理であるため、隠蔽され、外部からは見ることができない。並行処理では、オブジェクトが複数から同時にアクセスされることを許す。即ち、複数のものが状態を共有する。その結果、同時に状態の変更を要求されることがある。これは、競合と呼ばれる問題で、適切に解決しようとすると大変面倒なことになる。解決を提案した論文がたくさん出版されていることからも難しさが分かる。

実際、辛い経験もした。Webを用いて、経理にかかわる社員のだれもがアクセスできる会計システムを2000年の初めごろに開発した。それは、時間遅れのない会計情報が得られるということを売りにしたシステムであった。このため、トランザクションが生じたとき、会計データが瞬時にそれを反映することが要求された。その結果、データ更新の競合を避けるという面倒くさい作業が付与された。データの書き換えは一瞬であるのにもかかわらず、書き換えるための安全性を保障するために、余分な沢山のプログラムを必要とした。無事に完成はしたが、もう二度と関わりたくはないとも思った。

Haskellはイミュータブルである。従って、オブジェクト指向言語が持っていた並行処理を行う上での問題点は回避される。ただ、変数の値を変えることに馴染んでいるプログラマーは最初は戸惑うことと思う。変数を便利に多用していたことと思われるので、手足をもがれたように最初は身動きできないかもしれない。プログラムを再帰的に記述するというのが、イミュータブルな言語での解決策である。しかし、これに慣れるには時間のかかる人が多いであろう。

しかし、抽象化はとても大切な概念である。抽象化することによって、必要ではなさそうな細かすぎる問題から回避され、その本質だけに集中できるようになる。これにより、開発の時間は短縮され、信頼性は一段と高まる。デバッグのいらないプログラム開発を目指すこともできるようになる。

それでは、次回から圏論の話から始めよう。

上総国分尼寺を訪れる

古代史の勉強の一休みに、永井路子の『美貌の女帝』を読んでいる。

f:id:bitterharvest:20160929050548j:plain

主人公は氷高皇女(ひたかひめみこ、680-748)だ。女性の持統天皇の孫娘で、元正天皇となった(彼女の弟の軽皇子文武天皇になる。この姉弟の母の阿閇(あへ)皇女は、文武天皇がなくなった後、元明天皇となる。息子から母への継承である。彼女の母は蘇我姪娘(めいのいらつめ)である。氷高皇女は元明天皇の後をついで天皇になる。母から娘への継承である。なお、氷高皇女の父親は、草壁皇子であるが、夭折する。また、氷高皇女は結婚することはなかった)。この時代、女帝天皇が相次ぐ。皇太子が若いために、成人するまでの中継ぎとして、皇后が天皇になったといわれている(氷高皇女の場合は例外で娘)。永井路子のこの小説は、そうではなく、蘇我の娘として、男子の天皇に劣ることなく国家を統治してゆくという設定になっているようだ。まだ、読み始めなので、断定的なことは言えないのだが、これまでの視点とは違った見方に立って氷高皇女が描かれている。40ページほど読み進んだが、関心を持ちながら読んでいる。

元正天皇は、甥の聖武天皇(701-756)に譲位する(聖武天皇文武天皇の子)。聖武天皇が在位していた時期(724-749)、天然痘(737)が発生し、藤原の四兄弟(不比等の子供)を始めとして、要職についている人たちの多くが死亡するという惨事や、災害や世の乱れ(729年に長屋王の変、740年には藤原広嗣の乱)などもあり、聖武天皇は救いを求めて仏教に深く帰依する。741年には、国分寺建立の詔を出す。これにより、全国60余りの国にそれぞれ、国分寺が建立される。

国分寺は、男の僧のための国分僧寺と尼僧のための国分尼寺とがあり、それぞれの国に、対となって建立された。千葉県の市原市には、市役所を挟むようにして、上総(かずさ)国の国分僧寺国分尼寺の遺跡がある。今回(9月28日)は、この二つの寺の跡を訪ねることにした。
f:id:bitterharvest:20160929093819p:plain

市原まではかなり遠いのだが、4月に新宿に大きなバスターミナルが完成したので、そのバスタ新宿を見がてら、市原市役所行きの高速バスを利用することとした。

バスタ新宿新宿駅南口にある。改札を出ると目の前にその建物が見える。
f:id:bitterharvest:20160929101207j:plain

バスターミナルは4階にあるので、エスカレータで上り、少し進むと切符売り場に行きつく。
f:id:bitterharvest:20160929102253j:plain
最近の日本の盛り場はどこもそうなのだが、ここも例外ではなく、日本語以外の言語の方が優勢である。長距離バスの方が安いこともあって、利用する人も多いのだろう。先日、長野から出てきた同級生は、バス代がなんと片道1500円だったと、教えてくれた。高速バスの低価格に馴染んでいる人にとっては、このような価格は当たり前のようになっているのだろうが、新幹線の料金しか知らない身には、驚愕の価格である。全く競争にならないと思う一方で、やはり、事故も多いのだろうと昨今のバス事故を思い出して改めて考えさせられた。

案内所を出ると、長距離の高速バスが客を待ち構えている。
f:id:bitterharvest:20160929103207j:plain

僕のバスの乗り場はA3である。そこに行ってみると、ひとつ前の東京ディズニーランド行きのバスが丁度お客さんを乗せるところであった。人気がある場所への便ということで満席で出発した。市原市役所行きのバスを待つ場所も指定してあったが、そこには誰もいなかった。込み合うこともあるまいと思い、発車時間までしばらく間があったので、バスタ新宿からの景色を楽しむため窓に沿って散策した。

バスタ新宿側から新宿駅南口を見るとこんな感じである。
f:id:bitterharvest:20160929103724j:plain

立派になったものだと思う。高校生の頃はこの南口をほぼ毎日利用した。改札を出て甲州街道を四谷の方に下り、明治通りを渡ったところに僕たちの高校の正門があった。その頃の南口には駅舎が唯一つあるだけで、賑やかなところではなかった。

駅を出るとすぐに陸橋に差し掛かるが、橋の下には、まだ、蒸気機関車を見ることもできた。現在はこぎれいな電車が出入りしている。
f:id:bitterharvest:20160929104409j:plain

さて、出発時間の10時を少し遅れて到着したバスに乗り込んだ。乗客は6人、寂しいぐらいの乗客数であったが、示し合わせたかのように、ばらばらに適当な間隔をあけて席に着いた。途中、渋滞も少しあったが、1時間半ほどで目的地の市原市役所に着いた。

降車した後、市役所、国分寺中央公園を右側に見ながら国分尼寺へと向かう。しばらく行くと、次のような看板に行きついた。
f:id:bitterharvest:20160929111303j:plain

歩いている方向に間違いがないことを確認し、安堵する。さらにしばらく進むと、展示館にたどり着いた。ここを見るのは後回しにして、まずは、寺の跡を見ることにする。展示館のそばからの国分尼寺跡の風景は次のような感じ。
f:id:bitterharvest:20160929111800j:plain

綺麗に整備されていて、とても、のどかな感じである。周りには、掃除をしている人がいるだけで、観光客は一人も見つからない。一人でこの風景を独占でき、とても、いい気分になってさらに奥へと進んでみる。回廊がズームアップされる。
f:id:bitterharvest:20160929112206j:plain

さらに進むと中門が表れる。立派な門である。
f:id:bitterharvest:20160929112337j:plain

門の復元だけで2億円、回廊には12億円要したとのことであった。さらに回り込んで、回廊を外側から見る。
f:id:bitterharvest:20160929112941j:plain

金堂院は金堂基壇だけが復元されている。基壇から中門を望んだ風景は次のようである。
f:id:bitterharvest:20160929113339j:plain

回廊の内側は法隆寺を真似て復元したとのことであった。
f:id:bitterharvest:20160929113451j:plain

回廊で囲まれた庭は整然と石が敷き詰められていた。
f:id:bitterharvest:20160929113725j:plain
f:id:bitterharvest:20160929113950j:plain

展示館に戻って、テレビでの説明20分、模型を使っての説明10分を受けた。
f:id:bitterharvest:20160929114239j:plain

上総国分尼寺は、全国の国分尼寺の中でも最も規模が大きいそうで、その総面積は12万平米にも及ぶとのことである。現在、その1/3を市が所有し保護に努めているそうだ。

国分尼寺が建立されたころの日本は、律令制のもとでの中央集権国家で、天皇が強い力を持っていた時代である。地方は国ごとに支配され、それぞれの国には国府が置かれ、行政官である国司は中央から派遣されていた。また、それぞれの国は郡に分かれており、郡司には地方の豪族が就いていた。このため、実際の実務は豪族によって仕切られていた。

この当時の民衆たちは、律令制の基本である公地公民制で縛られていた。班田収授法に基づいて土地が割当てられたものの、租庸調によって一定の税金や使役が課せられた。

民衆たちは、豪族の監督の下で、国分寺建立の作業に従事させられたと思うが、随分と辛い仕事だったのではないかと考えるのだが、これを立証するような資料は残されていないので、想像の域を出ない。国分尼寺は、完成するまでに、20年の歳月を要したそうである。

尼寺が出来上がった当初は20人の尼僧が住み、その後、倍の40人になったそうである。雑用などをする人を含めて、全体では百人程度の人が住み、政務所と呼ばれるところで、日常の業務をしていたとのことである。

尼寺の概略は、次の看板によって知ることができる。
f:id:bitterharvest:20160929204405j:plain

尼寺を後にして、僧寺の遺跡に向かった。正面には、上総国分寺の石碑があった。
f:id:bitterharvest:20160929121254j:plain

中に入ると仁王門がある。
f:id:bitterharvest:20160929121728j:plain

坂東だなと思わせる塔もあった。将門塔である。将門伝説の一つなのだろう。
f:id:bitterharvest:20160929121904j:plain

さらに奥に進むと薬師堂がある。1716年に完成したとのこと。古風な趣のある建物である。
f:id:bitterharvest:20160929122123j:plain

少し外れたところに、国分僧寺の遺跡がある。70メートルの高さはあったであろうと推察されている七重塔の心礎が残っている。
f:id:bitterharvest:20160929134807j:plain
とても大きな石のため、このあたりでは採石できないので、50Km離れた鋸山から持ってきたのではと推察されている。心礎を囲むように、土台となる石が周りに配置されている。
f:id:bitterharvest:20160929135015j:plain

この周囲も市が所有しているのだろう。緑の野原になっていた。
f:id:bitterharvest:20160929135127j:plain

国分寺の遺跡が見つかっているのだから、国府の遺跡もあるだろうと尼寺の展示館で質問したところ、残念ながら見つかっていないとのことであった。

律令制の下では、税を都に納めることが課せられていた。納入するのは、民衆の役目である。当時、都と上総を結ぶのは東海道であった。当時の東海道は現在とは異なっている。相模国から、武蔵国に入るのではなく、海路で安房国を経由し、その後、陸路で上総国に向かっていた(武蔵国へは東山道を利用して上野国経由で至った)。市原のあたりにも古道らしきものは見つかっているようだが、当時の古道はたいそう立派な道路であったと予想される。しかし、そのような整備された古道を使ったとしても、上総から都まで、税を民衆が運ぶのは大変な夫役だったのではと思われる(納入する人夫は運脚と呼ばれた)。何人もの人が故郷に戻れなかったことであろう。

律令制を行き渡らせる中で、国府、郡衙国分寺、街道などの建設が行われたわけだが、当時の王朝の権力がいかに強かったかが、これらの遺構を見るたびに、認識させられる。

遺跡見学に満足し、バス停でおにぎりをほおばった後、帰路に着いた。高速道路で事故があり長い渋滞になっているという情報をチケット売り場で運よく入手したので、内房線五井駅から電車を利用した。快い疲れの中で、電車の中で軽い睡眠をとっての帰宅となった。

追:乙巳の変(645年)で蘇我入鹿は殺害され、親の蝦夷は自殺する。これにより、蘇我本宗家は滅びる。しかし、この変で、蘇我家の一族である蘇我倉山田石川麻呂(そがのくらのやまだのいしかわまろ)は、蝦夷・入鹿の滅亡を企てる側に立つ。この変の後、この一族は天皇家外戚(母方の親戚)となり、蘇我倉山田石川麻呂の血は氷高皇女(元正天皇)まで引き継がれる。(元正天皇が譲位した)聖武天皇の母は藤原不比等の娘の宮子、また、妻は同じ不比等の娘の光明子である(宮子とは異母姉妹)。これを機に、天皇家外戚蘇我から藤原へと移り変わる。

量子力学の世界を垣間見る(7):シュレディンガー方程式

昨日(9月22日)は、中学時代の同級生と大相撲秋場所を見に行った。
f:id:bitterharvest:20160923064550j:plain

横綱昇進がかかっていた稀勢の里が、前の日に敗れて3敗を喫したため、今場所の興味が薄れてしまった直後の観戦となった。他方で、先場所、休場した豪栄道が、信じられないほどの充実した相撲を取り続け、初めての優勝を勝ち取れるのかという方に興味が移ってきた日でもあった。

相撲の観戦は初めてではない。30年前に数回訪れたことはあるが、枡席が狭かったことが印象に残っている。週の初めごろから、何となく腰が重いので、あの狭いところに座るのはつらいだろうなと思い続けていたが、観戦の2日前にその対策をブログで調べていたら、アウトドア用のチェアマットがよいというのを見つけたので早速購入して、この日に備えた。
f:id:bitterharvest:20160923071049j:plain

席に着くときに、お茶屋さんが隣の席は来ないようなので、使っても大丈夫でしょうというので、連続した枡席で、それぞれ2名のゆったりした空間で、直前に入手したチェアマットを利用するまでもなく、3段目からの相撲を楽しみ始めた。十両の相撲に入ったころに、お茶屋さんの勘違いだったようで、我々が占拠している席の正規のお客さんが出現した。我々は、お茶屋さんが大丈夫だといっていたなどとぶつぶつ言いながら、狭い枡席に4人を詰め込む羽目になった。これが当たり前の状態なのだが、直前までの自由な空間と比較すると、身動きができないほど狭い。

チェアマットを利用したのだが、後ろの人にぶつかりそうになるので、その機能を生かすことができない。幸い、席の前は通路になっていた。人っ子一人通れるほどの狭い通路なのだが、通行人がいない時を見計らって、通路に足を投げ出して少しでも腰と足に負担がかからないように心掛けた。後ろに座って、あぐらをかいていた同級生の一人は、途中で足がつってしまい、痛い痛いと騒いでいた。また、前の方に座ったもう一人は、僕と同じように足を通路に投げ出している時、通行人にわざと踏まれたようで、こちらも不要な痛みをもらうこととなった。

行事や呼び出しの衣装はその階級によって変わってゆく。三段目あたりでは、裸足で、脛も露出している。いかにも使いぱっしりの小僧という感じだ。横綱大関を裁くような役柄になると、その相続は甚だ立派である。立行司ともなると短刀をさしている。このように、古いしきたりを守り続ける大相撲なのだが、畳での生活が日常的ではなくなった我々にとっては、枡席はいささか窮屈すぎるように思う。古い世代の男性4人であったので救われた部分もあるが、体格がよくなった若い世代の男性4人が枡席に押し込まれた姿は、想像するのさえ気持ちが悪い。

さて、量子力学の話に一区切りつけたいので、今回の記事は、量子力学で中心をなしているシュレディンガー方程式の話である。

12.シュレディンガー方程式

粒子が相互に交換される記事で、それを表すハミルトニアンを次のようにした。
\begin{eqnarray}
\hat{H}=-g\sum_{l=-\infty}^\infty (\hat{a}^\dagger_{l+1} \hat{a}_l + \hat{a}^\dagger_{l} \hat{a}_{l+1})
\end{eqnarray}

12.1 運動エネルギー

エネルギーは、運動エネルギーとポテンシャルエネルギーで表される。上記の式が運動エネルギーだけを表すようにするために、少し変形する。上記の式では、二つの粒子が存在するので、これを打ち消すことを考える。このためには、二つの粒子を引き去ればよいと考えられるので、同じ位置で相互に交換される二つの粒子を前の式から差し引いた次の式を考える。
\begin{eqnarray}
\hat{H}'=-g\sum_{l=-\infty}^\infty (\hat{a}^\dagger_{l+1} \hat{a}_l + \hat{a}^\dagger_{l} \hat{a}_{l+1} - 2\hat{a}^\dagger_{l} \hat{a}_{l})
\end{eqnarray}

波数の1次元空間中での1粒子状態\(|\kappa> = \displaystyle \sum=m A e^{i\kappa m}\hat{a}^\dagger_m|0>\)に作用させると
\begin{eqnarray}
\hat{H}' |\kappa>=g(2sin \frac{\kappa}{2})^2 | \kappa>
\end{eqnarray}
となる。これより、\(|\kappa>\)が\(\hat{H}'\)の固有状態であることが分かる。

また、\(k=\kappa / d\)なので、分散関係
\begin{eqnarray}
E' = g(2sin \frac{\kappa}{2})^2=g(2sin \frac{k}{2} d)^2
\end{eqnarray}
である。これより、\(k=0\)の時、即ち、粒子が移動していないとき、エネルギーは0となる(これから、上記の式にはポテンシャルエネルギーが0であることが確認された)。

\begin{eqnarray}
\hat{H}=-g\sum_{l=-\infty}^\infty (\hat{a}^\dagger_{l+1} \hat{a}_l + \hat{a}^\dagger_{l} \hat{a}_{l+1} - 2\hat{a}^\dagger_{l} \hat{a}_{l})
\end{eqnarray}
を実空間に移すと、
\begin{eqnarray}
\hat{H}=\int_{-\infty}^\infty \hat{a}^\dagger(x) \left[ - \frac{\hbar^2}{2m} \frac{\partial^2}{\partial x^2} \right] \hat{a}(x) dx
\end{eqnarray}
なお、上の式で\(m\)は1粒子の重さである。

また、波数空間では
\begin{eqnarray}
\hat{H}=\int_{-\infty}^\infty \left[ \frac{(\hbar k)^2}{2m} \frac{\partial^2}{\partial x^2} \right] \hat{a}^\dagger(k) \hat{a}(k) dk
\end{eqnarray}

12.2 ポテンシャルエネルギー

ポテンシャルを\(V(x)\)で与えることにしよう。位置や運動量を表す演算子を考えてきたときと同じように、ポテンシャルを表す演算子を次のように用意しよう。
\begin{eqnarray}
\hat{V} = \int_{-\infty}^\infty V(x) \hat{a}^\dagger(x) \hat{a}(x) dx
\end{eqnarray}

位置\(x'\)に粒子が1つある状態\(\hat{a}^\dagger(x') | 0 > \)に\(\ \hat{V}\)を作用させると、

\begin{eqnarray}
\hat{V}\hat{a}^\dagger(x') | 0 > &=& \left[ \int_{-\infty}^\infty V(x) \hat{a}^\dagger(x) \hat{a}(x) dx \right] \hat{a}^\dagger(x') | 0 > \\
&=& V(x') \hat{a}^\dagger(x') | 0 >
\end{eqnarray}
となる。

12.3 シュレディンガー方程式

それでは、運動量とポテンシャルを共に含んだ演算子を考えることにしよう。
\begin{eqnarray}
\hat{H} &=&\int_{-\infty}^\infty \left[ - \frac{\hbar^2}{2m} \hat{a}^\dagger(x) \frac{\partial^2}{\partial x^2} + V(x) \hat{a}^\dagger(x) \hat{a}(x) dx \right] \hat{a}(x) dx \\
&=& \hat{T} + \hat{V}
\end{eqnarray}

状態\(\)の時間発展方程式は次の式で表すことができた。
\begin{eqnarray}
i \hbar \frac{d}{dt}|\Psi(t)>=\hat{H}|\Psi(t)>
\end{eqnarray}

そこで、この式に先の演算子を代入すると、次のシュレディンガー方程式を得ることができる(なお、\(\psi(y, t)\)は波動関数である)。

\begin{eqnarray}
i \hbar \frac{\partial}{\partial t} \psi(y, t) = \left[ - \frac{\hbar^2}{2m} \frac{\partial^2}{\partial y^2} + V(y) \right] \psi(y,t)
\end{eqnarray}

随分と沢山の記事を書いたが、漸くシュレディンガー方程式までたどり着いた。

Haskellとの関連で残されている課題は、ケットとブラを連続な空間に拡張することである。この時、将来への拡張を考えて、1次元空間ではなく3次元空間への適用を考慮しておく必要がある。
現時点では、3次元の複素数のベクトル空間が考えられるが、これについては、また、時を改めて記事を書くこととしたい。

量子力学の世界を垣間見る(6):ハミルトニアンの活用

この3月に退職して以来、これまで専門としてきた情報科学の研究から離れて、日本の古代史(平安時代の終わりまで)を勉強している。

僕が高校生だった頃は、社会科の科目には、社会、世界史、日本史、地理の4科目が設けられていた。その内で、社会は必修で、残りの3科目の中から2科目を選択し、合わせて3科目を学習するようになっていた。

僕が入学したいと思っていた大学は、社会は受験科目に含まれておらず、世界史、日本史、地理の中から2科目を選んで受験することとなっていた。もともと地理は好きだったので、中学生の頃にはかなりのことが身についていて、そのまま、大学受験をしても大丈夫な程度の知識を有していた。このため、知識の量を増やすという観点に立てば、世界史と日本史を選択して高校時代を過ごせばよかったのだが、大学へ入学できることを最優先にして、高校では、地理と世界史を学んだ。そのため、日本史を学ぶ機会を失ってしまったため、この歳になって学びなおしてみたいと思うようになった。

幸いなことに、古代史の研究は、新たな遺跡の発見や発掘などの恩恵もあって、近年、大きな変貌を遂げている。もし、高校時代に学んでいたとすると、その時の知識が邪魔をして、今日の新しい考え方が素直に入ってこなかったのではないかと思い、変な意味で、高校時代に日本史を学ばなかったことに今感謝している。

しかし、新しい分野を学ぶということは大変なことで、頭の中にその分野の体系を形作るまでに、受験勉強にも勝る努力を必要とする。最近は、インターネットのおかげでとても便利な世の中になり、分からないことがあった時には、図書館を訪れるまでもなく、ウィキペディアで調べれば、瞬時に情報を得ることができる。知りたいと思っていた情報を得ることがかくも簡単になったので、学ばなくてもいいのだという暴言を吐く人もいるようだが、それに関連する知識をある程度有していないと、折角、情報を得たとしてもそれを理解することはできない。

ウィキペディアを利用するときは、それを確認するときという程度にまでなっていれば、その分野についての体系が頭の中にちゃんと構成されているのだろうと思う。この状態をめざして、そろそろ、半年になるが、勉強を続けている。

さて、勉強の仕方だ。一つは専門家から教えてもらった方が、良いだろうということで、カルチャセンターを利用して、古代史の専門家の講義を聞いている。専門家はその分野のツボを心得ているので、何となく言った言葉に深い知見が含まれていることが時々ある。沢山の知識を持っている素人の研究家と専門家の違いは、学問の核心を知っているかどうかだと思うが、その違いがちょっとした話の中に出てくることが多い。その意味では、その分野で業績を上げている人の話を聞くことは欠かすことができない。

二つ目は乱読である。その分野についての体系を得るためには、できるだけ、沢山の本を読んで、その概略を知ることが必要だと考えている。その時、あまり細かいことを気にする必要がない。自分の中で、大雑把なストーリーを構成していくのがよい。

沢山の本を読むことになるので、全ての本を購入したのでは費用もかさむし、その中に、欲しくない本も紛れ込むことになる。そこで、図書館を利用することを勧める。住んでいる市が小さいとそこの図書館で読みたいと思う本を見つけ出すことは大変だろうと思うが、幸い、僕の住んでいるところは人口が40万人の中規模な市なので、図書館の蔵書数もそれなりにある。このため、読みたいなと思った本が手に入る可能性は高い。4月から図書館の利用を始めたが、借りた本は15冊程度になっている。

もっとも、本当に良さそうな本は、電子書籍で購入しているので、すでに古代史の関連の本は25冊を超えて読んでいるものと思う。これだけ読むと、どの著者の本を読めばよいかが自ずと分かってくる。幸いに、カルチャーセンターでの先生は、『権記』、『御堂関白記』、最近では『小右記』の現代語訳を出版するなど、精力的に研究を行っている方で、優れた専門家の一人であることが分かった。

三つ目は精読なのだが、まだ、この段階には入っていない。

さて、古代史を勉強する傍らで、量子力学Haskellで表現する作業を続けている。時間を見つけての仕事となるので、飛び飛びになってしまう。前回は、発生演算子と消滅演算子を連続系で表現したので、今回は、これを用いて、ハミルトニアンの利用法を考えてみよう。

11.ハミルトニアンの活用

0)予備知識

1粒子状態で粒子を発見する確率を表す波動関数を\(\psi(x)\)で表すことにしよう。そして、これは規格化されているとする。即ち\(\psi(x)\psi^*(x)=1\)と仮定することにしよう。粒子の位置\(x\)を、粒子が発見される場所の平均値\(< x >\)で表すことにする。この時、\(< x >\)は、次の式で表すことができる。
\begin{eqnarray}
< x > = \int^{\infty}_{-\infty} \psi^*(x) x \psi(x)dx = \int^{\infty}_{-\infty} \rho (x) x dx
\end{eqnarray}

今、\(\rho (x)\)は確率密度関数と呼ばれるが、これがが次のガウス関数であったとしよう。
\begin{eqnarray}
\rho (x) = |A|^2 e ^{-x^2/D^2} \\
A=\frac{1}{\sqrt{D \sqrt{}\pi}}
\end{eqnarray}
このとき、粒子の位置は次のようになる。
\begin{eqnarray}
\int^{\infty}_{-\infty} |A|^2 e ^{-x^2/D^2} x dx = 0
\end{eqnarray}
なお、ガウス関数は次の図のように釣り鐘型をしており、\(\rho (x)\)は\(x=0\)で最大となり、\(x\)の絶対値が大きくなるとどんどん0に近付く。
f:id:bitterharvest:20160921145604p:plain

粒子の位置は\(x\)で、運動量は\(p=\hbar k\)、運動エネルギーは\(E=\frac{p^2}{2m}=\hbar \omega\)で表わさる。そこで、これらの平均値を考えることにしよう。

1) 位置を表す演算子

今、演算子\(\hat{X}\)を次のように定義しよう。
\begin{eqnarray}
\hat{X} = \int^{\infty}_{-\infty} x \hat{a}^\dagger(x) \hat{a}(x) dx
\end{eqnarray}

\(\hat{X}\)を\(\hat{a}^\dagger(x')|0>\)に作用させると

\begin{eqnarray}
&&\hat{X}\hat{a}^\dagger(x')|0> \\
&=& \int^{\infty}_{-\infty}[ x \hat{a}^\dagger(x) \hat{a}(x)] \hat{a}^\dagger(x') dx |0> \\
&=& \int^{\infty}_{-\infty} x \hat{a}^\dagger(x) [\hat{a}(x) \hat{a}^\dagger(x')] dx |0> \\
&=& \int^{\infty}_{-\infty} x \hat{a}^\dagger(x) \delta(x-x')] dx |0> \\
&=& x' \hat{a}^\dagger(x') |0>
\end{eqnarray}
となるので、\(\hat{a}^\dagger(x')|0>\)は、\(\hat{X}\)の固有状態であり、その固有値は\(x'\)である。

そこで、\(\hat{X}\)を1粒子状態の重ね合わせ\(\int^{\infty}_{-\infty} \psi (x) \hat{a}^\dagger(x) |0> dx \)に作用させると
\begin{eqnarray}
\hat{X} \int^{\infty}_{-\infty} \psi (x) \hat{a}^\dagger(x) |0> dx  = \int^{\infty}_{-\infty} \psi (x) x \hat{a}^\dagger(x) |0> dx
\end{eqnarray}
となる。これより、\(\psi (x)\)が規格化された波動関数である時、
\begin{eqnarray}
< \hat{X} > = \left[ < 0 | \int^{\infty}_{-\infty} \psi^* (x) \hat{a}(x) dx \right] \hat{X} \left[ \int^{\infty}_{-\infty} \psi (x') \hat{a}^\dagger(x') dx' |0>\right] = 0
\end{eqnarray}
となり、\(\hat{X} = \int^{\infty}_{-\infty} x \hat{a}^\dagger(x) \hat{a}(x) dx\)が位置を表す演算子であることが分かる。

なお、詳しい式の展開を知りたいときは、西野友年著『場の理論』を参考にして欲しい。以下同じである。

2)運動量を表す演算子

1粒子の運動量は、波数\(k\)を用いると、\(\hbar k\)となる。そこで、位置の平均値を求めた時と同じように、運動量の平均値を求めるために、\(x\)のところを\(\hbar k\)で置き換えて、次のような演算子を用意しよう。
\begin{eqnarray}
\hat{P} = \int^{\infty}_{-\infty} \hbar k \hat{a}^\dagger(k) \hat{a}(k) dk
\end{eqnarray}

そこで、前と同じように、\(<\hat{P}>\)を考えてみよう。なお、波数の空間での規格化された波動関数を\(\phi (k)\)とする。
\begin{eqnarray}
< \hat{P} > = \left[ < 0 | \int^{\infty}_{-\infty} \phi^* (k) \hat{a}(k) dk \right] \hat{P} \left[ \int^{\infty}_{-\infty} \phi (k') \hat{a}^\dagger(k') dk' |0>\right]
\end{eqnarray}
式を変形すると
\begin{eqnarray}
< \hat{P} > = <\hbar k>
\end{eqnarray}
となる。詳しくは、西野友年著『場の理論』を参考にして欲しい。

また、波数空間ではなく、実空間を用いて表すこともできる。この時は、
\begin{eqnarray}
\hat{P} = \int^{\infty}_{-\infty} \hat{a}^\dagger(x) \left[ -i \hbar \frac{d}{dx} \right] \hat{a}(x) dx
\end{eqnarray}
となる。

また、波数空間での波動関数を以下に示すガウス関数であると仮定する。
\begin{eqnarray}
\phi(k') = A D e^{- D^2 (k - k')^2 / 2 } = \phi^*(k')
\end{eqnarray}

この時、運動量の平均値は
\begin{eqnarray}
< \hat{p} > = \hbar k
\end{eqnarray}
となる。

3)運動エネルギーを表す演算子

1粒子を取り上げてきたので、今度は、2粒子の場合を取り上げてみることにしよう。複数の粒子がある場合には、ボーズ粒子であるのかフェルミ粒子であるのかが問題になる。ここでは、フェルミ粒子としよう。長さが\(L\)でポテンシャルが存在せず、運動エネルギーだけが存在する場合を考えることにしよう。運動エネルギーは\(E=\frac{p^2}{2m}=\frac{{(hk)}^2}{2m}\)で表すことができる。また、また、長さが有限な値\(L\)とすると、波数は有限個の異なる整数となる。このため、波数については積分ではなく、和となる。このことに注意して、位置を求めた時の演算子と同じように、次の演算子\(\hat{H}\)を定義する。
\begin{eqnarray}
&&\hat{H} == \displaystyle \sum_k \frac{(\hbar k)^2}{2m}\hat{c}^\dagger_k \hat{c}_k
\end{eqnarray}

二つの粒子が波数\(k_1\)と\(k_1\)を有する場合の状態\( \hat{c}^\dagger_{k_1} \hat{c}^\dagger_{k_2} | 0> \)にこの演算子を作用させてみよう。
\begin{eqnarray}
&&\left[\displaystyle \sum_k \frac{(\hbar k)^2}{2m}\hat{c}^\dagger_k \hat{c}_k \right] \hat{c}^\dagger_{k_1} \hat{c}^\dagger_{k_2} | 0> \\
&=& \left[\frac{(\hbar k_1)^2}{2m}+\frac{(\hbar k_2)^2}{2m} \right] \hat{c}^\dagger_{k_1} \hat{c}^\dagger_{k_2} | 0>
\end{eqnarray}

これから、それぞれの粒子のエネルギーの和となることが分かる。

有限の長さの時は、波数が離散的になるので、これまで展開してきた離散的なモデルでのHaskellを用いて、表現することができる。

2粒子状態に対する波動関数は、スレーター行列式で表すことができるが、これについては、西野友年著『場の理論』を参考にして欲しい。

量子力学の世界を垣間見る(5):離散系から連続系へ

これまで、Haskellと結びつけながら量子力学の世界を説明してきた。そのため、Haskellに邪魔されて量子力学の世界が分かりにくくなったかもしれない。

そこで、この記事では、これまでの話をまとめながら、量子力学の世界を、離散系から連続系へと展開させてみたい。

9.離散系

ここは、量子力学の初歩をHaskellで学ぶの中の、ブラとケットおよび重ね合わせの記事の要約である。

これまで、量子力学の世界を一次元空間の離散系として見てきた。この離散系は、下図に示すように、粒子は格子点で区切られた場所に存在する。
f:id:bitterharvest:20160915041232p:plain
この時、格子点間の距離を\(d\)とし、格子点を区別できるように番号\(l\)をつけて考えた。

粒子の状態は、ケットあるいはブラで表した。真空状態を\( | 0 > \)あるいは\( < 0 | \)で表した。状態からは、演算子を作用させることで新しい状態を作ることができる。演算子には、発生演算子\(\hat{a}^\dagger_l\)と消滅演算子\(\hat{a}_l\)がある。\(\hat{a}^\dagger_l\)は\(l\)番目の格子点に一つの粒子を生成し、\(\hat{a}_l\)は\(l\)番目の格子点から一つの粒子を消滅する。

\(l\)番目の格子点に粒子が一つ存在している状態は\(\hat{a}^\dagger_l | 0 > \)と表すことができる。量子力学では、一つの粒子しか存在しないのだが、それが存在する格子点の場所を特定できないという現象が存在する。しかし、観察すると粒子はどこかの格子点で見つかる。そこで、観察したときに、番号\(l\)で見つかる確率(頻度)を\(\varphi_l\)とする。そして、この現象を重ね合わせ状態\(\hat{\Psi}^\dagger | 0 > = \displaystyle \sum_l \psi_l\hat{a}^\dagger_l | 0 >\)で表す。あるいは、ブラで表現するとこの状態は\(< 0 | \hat{\Psi} = < 0 | \displaystyle \sum_l \hat{a}_l \psi^*_l \)である。なお、\(\psi_l\)と\(\psi^*_l\)は互いに共役な数である。

また、\(< 0 | \hat{\Psi} \hat{\Psi}^\dagger | 0 > = < 0 |\displaystyle \sum^\infty_{l=-\infty} \hat{a}_l \psi^*_l \psi_l \hat{a}^\dagger_l | 0 > = 1\)の時、即ち、\(\displaystyle \sum^\infty_{l=-\infty} \psi^*_l \psi_l = 1\)の時、確率\(\varphi_l\)は規格化されているという


9.1 飛び移る粒子の固有状態

ここは、有限系を説明した記事の要約である。

図のように粒子が隣の格子点に飛び移るモデルを考えてみよう。
f:id:bitterharvest:20160826082049p:plain

これに対するハミルトニアンは次のようになる。
\begin{eqnarray}
\hat{H}=-g \sum_l(\hat{a}^l_{l+1}\hat{a}_{l}+\hat{a}^l_{l}\hat{a}_{l+1})
\end{eqnarray}


そして、一つの粒子が重なり合っている状態\(| \Psi >= \displaystyle \sum_l\psi_l(t) \hat{a}^\dagger_l |0>\)に\(\hat{H}\)を作用させると、時間発展方程式\(i \hbar \frac{d}{dt} | \Psi(t)> = \hat{H}| \Psi(t)>\)は次のようになる。
\begin{eqnarray}
i \hbar \frac{d}{dt} \displaystyle \sum_l \psi_l(t)\hat{a}^\dagger_l |0> = -g \displaystyle \sum_l [\psi_{l-1}(t)+\psi_{l+1}(t)] \hat{a}^\dagger_l |0>
\end{eqnarray}


これから、次の微分方程式をえる。
\begin{eqnarray}
i \hbar \frac{d}{dt} \psi_m(t) = -g[ \psi_{m -1}(t)+\psi_{m +1}(t)]
\end{eqnarray}
この方程式の解は、
\begin{eqnarray}
\psi_l(t) = Ae^{-iwt}e^{i\kappa l}
\end{eqnarray}
で、\(\kappa\)は隣り合う格子点での位相差である。

また、上の解は次の分散関係を満たさなければならない。
\begin{eqnarray}
\psi_l(t) = Ae^{-iwt}e^{i\kappa l}=\phi_\omega(t) \varphi_{\kappa l}
\end{eqnarray}
ここで、
\begin{eqnarray}
\phi_\omega(t) &=& e^{-iwt} \\
\varphi_{\kappa l} &=& A e^{i\kappa l}
\end{eqnarray}

解は、時間に関係する関数\(e^{-iwt}\)と位置に関する\(e^{ikl}\)の積によって与えられる。

注意:任意の演算子\(\hat{O}\)に対して、平均値\(<\Psi(t)|\hat{O}|\Psi(t)>\)が時間\(t\)に関係しないとき、状態\(|\Psi(t)>\)を定常状態と呼ぶ。また、\(|\Psi(t)>\)が時間発展方程式の解であるとき、これを定常解という。従って、\(\psi_l(t) = Ae^{-iwt}e^{i\kappa l}\)は定常解である。

9.2 \(e^{i\kappa l}\)を係数に持つ重ね合わせ状態

\(e^{i\kappa l}\)を係数に持つ重ね合わせ状態を考えることにする。
\begin{eqnarray}
\displaystyle \sum_l \phi_{\kappa l} \hat{a}^\dagger_l |0> = \displaystyle \sum_l A e^{i\kappa l} \hat{a}^\dagger_l |0>
\end{eqnarray}

これに、飛び移る粒子に対するハミルトニアンを作用させると、

\begin{eqnarray}
\hat{H} \displaystyle \sum_l A e^{i\kappa l} \hat{a}^\dagger_l |0> = -2g \cos \kappa \displaystyle \sum_l A e^{i\kappa l} \hat{a}^\dagger_l |0> = E \displaystyle \sum_l A e^{i\kappa l} \hat{a}^\dagger_l |0>
\end{eqnarray}

但し、\(E=-2g \cos \kappa \)とする。なお、\(E\)は物理的な量としてはエネルギーである。

\(-2g \cos \kappa\)が係数なので、\(\displaystyle \sum_l A e^{ikl} \hat{a}^\dagger_l |0>\)は\(\hat{H}\)の固有状態、\(E=-2g \cos \kappa\)は\(\hat{H}\)の固有値である。

固有状態が見つかると、定常状態はこれより簡単に求めることができる。いま、固有状態を\(|\varepsilon>\)とすると、定常状態は
\begin{eqnarray}
| \Psi(t) > = e^{-i(E / \hbar)t} | \varepsilon> =e^{-iwt} | \varepsilon>
\end{eqnarray}
となる。

注意:ある状態\(|\Psi(t)>\)に演算子\(\hat{O}\)を作用させたとき、
\begin{eqnarray}
\hat{O} | \Psi(t) > = \lambda | \Psi(t) >
\end{eqnarray}
となるとき、 \(\lambda\)を固有値、\( | \Psi(t) > \)を固有状態という。なお、\(\lambda\)は係数である。

9.3 周期系

ここは、周期系を説明した記事の要約である。
f:id:bitterharvest:20160827104401p:plain
周期系での飛び移る粒子のハミルトニアンは次のようになる。
\begin{eqnarray}
\hat{H}_{periodic}=-g\sum_{l=0}^{N-1} (\hat{a}^\dagger_{l+1} \hat{a}_l + \hat{a}^\dagger_{l} \hat{a}_{l+1})
\end{eqnarray}
なお、\(l=N\)の時、\(l=0\)である。

このハミルトニアン\( \hat{H}_{periodic} \)の固有状態は平面波状態
\begin{eqnarray}
\displaystyle \sum^{N-1}_{l=0} \phi_{\kappa l} \hat{a}^\dagger_l |0> = \displaystyle \sum^{N-1}_{l=0} A e^{i\kappa l} \hat{a}^\dagger_l |0>
\end{eqnarray}
である。ここで、
\begin{eqnarray}
\kappa &=& \frac{2 \pi m}{N} \\
k= \frac{2 \pi m}{Nd} &=& \frac{2 \pi m}{L}
\end{eqnarray}
なお、\(m\)は整数である。これの取りうる値については後で述べる。また、 \(\kappa\)と\(k\)は離散的な値しか取れないことに注意。


固有状態が波数で表わされているので、この状態を\(|\kappa >\)で表すこととする。即ち、
\begin{eqnarray}
|\kappa > = \displaystyle \sum^{N-1}_{l=0} \phi_{\kappa l} \hat{a}^\dagger_l |0> = \displaystyle \sum^{N-1}_{l=0} A e^{i\kappa l} \hat{a}^\dagger_l |0>
\end{eqnarray}
とする。

また、規格化して\(A\)の値を得ておこう。
\begin{eqnarray}
\displaystyle \sum^{N-1}_{l=0} P'_{l} &=& 1 \\
P'_{l}=\varphi^*_{\kappa l}\varphi_{\kappa l} &=& A^2
\end{eqnarray}
より、\(A=\frac{1}{\sqrt N}\)
となる。

これより平面波状態は次のようになる。ついでにブラの方も示しておこう。
\begin{eqnarray}
|\kappa > = \displaystyle \sum^{N-1}_{l=0} \phi_{\kappa l} \hat{a}^\dagger_l |0> &=& \displaystyle \sum^{N-1}_{l=0} \frac{1}{\sqrt N} e^{i\kappa l} \hat{a}^\dagger_l |0> \\
<\kappa | = \displaystyle \sum^{N-1}_{l=0} <0| \hat{a}_l \phi^*_{\kappa l} &=& \displaystyle \sum^{N-1}_{l=0} <0| \hat{a}_l \frac{1}{\sqrt N} e^{-i\kappa l}
\end{eqnarray}

\begin{eqnarray}
<\kappa |\kappa ' > = \delta_{kk'}
\end{eqnarray}
である。\(\delta_{kk'}\)は\(k=k'\)の時1、そうでない時0である。従って、平面波状態は直交している。

平面波状態\( |\kappa > = \displaystyle \sum^{N-1}_{l=0} \phi_{\kappa l} \hat{a}^\dagger_l |0>\)は、生成演算子\(\hat{a}^\dagger_l\)の線形結合になっている。そこで、線形結合の部分を\(a^\dagger_{[\kappa]}\)で表すと、次のようになる。
\begin{eqnarray}
\hat{a}^\dagger_{[\kappa]} = \displaystyle \sum^{N-1}_{l=0} \phi_{\kappa l} \hat{a}^\dagger_l &=& \displaystyle \sum^{N-1}_{l=0} \frac{1}{\sqrt N} e^{i\kappa l} \hat{a}^\dagger_l \\
\hat{a}_{[\kappa]} = \displaystyle \sum^{N-1}_{l=0} \phi^*_{\kappa l} \hat{a}_l &=& \displaystyle \sum^{N-1}_{l=0} \frac{1}{\sqrt N} e^{-i\kappa l} \hat{a}_l
\end{eqnarray}

\(\hat{a}^\dagger_{[\kappa]}\)と\(\hat{a}_{[\kappa]}\)は波数\(k=\kappa / d\)の生成演算子、消滅演算子と考えることができる。

逆に\( \hat{a}^\dagger_{[\kappa]}\)と\(\hat{a}_{[\kappa]}\)を用いて、\( \hat{a}^\dagger_l\)と\(\hat{a}_l\)を次のように表すこともできる。
\begin{eqnarray}
\hat{a}^\dagger_l = \displaystyle \sum_\kappa \phi^*_{\kappa l} \hat{a}^\dagger_l &=& \displaystyle \sum_\kappa \frac{1}{\sqrt N} e^{-i\kappa l} \hat{a}^\dagger_{[\kappa]} \\
\hat{a}_l = \displaystyle \sum_\kappa \phi_{\kappa l} \hat{a}_l &=& \displaystyle \sum_\kappa \frac{1}{\sqrt N} e^{i\kappa l} \hat{a}_{[\kappa]}
\end{eqnarray}
なお、\(\kappa = \frac{2 \pi m}{N}\)で\(m=0,1,..,N-1\)の値をとる(他にも\(m\)の取り方はある。これは任意の\(i\)に対して\(j=m \times i + j\)なので、\(N\)個の独立な\(m\)の取り方はいかようにもなる。例えば、連続した\(m\)個の整数を取ってもよい) 。

10.連続系

10.1 周期系

今までは離散系で考えていたが、連続で考えたほうが分かりやすい場合もあるし、もっといいことには、数学的には取り扱いやすい場合も多い(逆に、情報科学の場合には面倒くさいことになる)。

波数の生成演算子\(\hat{a}^\dagger_{[\kappa]}\)を一次元の連続な空間で定義することとしよう。式の展開は大幅に省くので、詳しくは、西野友年著『場の理論』を参考にして欲しい。

\(x=ld\)とすると次の関係が成り立つ。
\begin{eqnarray}
\varphi_k(x)=\varphi_{kl} / \sqrt {d} \\
\hat{a}^\dagger(x) \leftarrow \hat{a}^\dagger_l / \sqrt {d} \ \ (d \leftarrow 0) \\
\hat{a}(x) \leftarrow \hat{a}_l / \sqrt {d} \ \ (d \leftarrow 0)
\end{eqnarray}

これを利用すると、
\begin{eqnarray}
&&\hat{a}^\dagger_{[\kappa]} \\
&=& \sum^{N-1}_{l=0} \varphi_{kl}\hat{a}^\dagger_{l} \\
&=& d \sum^{N-1}_{l=0} \frac{\varphi_{kl}}{\sqrt {d}} \frac{\hat{a}^\dagger_{l}}{\sqrt {d}} \\
&\rightarrow& d \sum^{N-1}_{l=0} \varphi_k (ld) \hat{a}^\dagger(ld) \ \ (d \leftarrow 0) \\
&=& \int^L_{x=0} \varphi_k (x) \hat{a}^\dagger(x) dx \\
&=& \int^L_{x=0} \frac{e^{ikx}}{\sqrt{L}} \hat{a}^\dagger(x) dx
\end{eqnarray}

上記の最後の式は、\(\kappa\)ではなく、波数\(k\)で表わされているので、\(\hat{a}^\dagger_{[\kappa]}\)を\(\hat{a}^\dagger_k\)で表すことにすると、
\begin{eqnarray}
\hat{a}^\dagger_k = \int^L_{x=0} \frac{e^{ikx}}{\sqrt{L}} \hat{a}^\dagger(x) dx
\end{eqnarray}

同様に
\begin{eqnarray}
\hat{a}_k = \int^L_{x=0} \frac{e^{-ikx}}{\sqrt{L}} \hat{a}(x) dx
\end{eqnarray}

また、
\(\hat{a}^\dagger(x)\)と\(\hat{a}(x)\)もそれぞれ波数での発生演算子の線形結合と消滅演算子での線形結合で表すことができる。これらをまとめたのが以下の表である。

\begin{array}{|c|c|c|}
\hline
& 離散系 & 連続系 \\
\hline
位& \hat{a}^\dagger_l = \displaystyle \sum^{N-1}_{l=0} \frac{e^{-i \kappa l}}{\sqrt{N}} \hat{a}^\dagger_{[\kappa]} & \hat{a}^\dagger(x) = \displaystyle \sum_k \frac{e^{-i k x}}{\sqrt{L}} \hat{a}^\dagger_k \\
置& \hat{a}_l = \displaystyle \sum^{N-1}_{l=0} \frac{e^{i \kappa l}}{\sqrt{N}} \hat{a}_{[\kappa]} & \hat{a}(x) = \displaystyle \sum_k \frac{e^{i k x}}{\sqrt{L}} \hat{a}_k \\
\hline
波& \hat{a}^\dagger_{[\kappa]} = \displaystyle \sum^{N-1}_{l=0}\frac{e^{i \kappa l}}{\sqrt{N}}\hat{a}^\dagger_l & \hat{a}^\dagger_k = \displaystyle \int^L_{x=0}\frac{e^{i k x}}{\sqrt{L}}\hat{a}^\dagger (x) dx \\
数& \hat{a}_{[\kappa]} = \displaystyle \sum^{N-1}_{l=0} \frac{e^{-i \kappa l}}{\sqrt{N}} \hat{a}_l & \hat{a}_k = \displaystyle \int^L_{x=0} \frac{e^{-i k x}}{\sqrt{L}} \hat{a}(x) dx \\
\hline
\end{array}

10.2 無限系

有限ではなく無限になった時も発生演算子、消滅演算子を求めることができる。ここでは結果だけを表に記載する。詳しくは西野友年著『場の理論』を参考にして欲しい。

\begin{array}{|c|c|c|}
\hline
& 有限 & 無限 \\
\hline
位 & \hat{a}^\dagger(x) = \displaystyle \sum_k \frac{e^{-i k x}}{\sqrt{L}} \hat{a}^\dagger_k & \hat{a}^\dagger (x) = \displaystyle \int^\infty_{-\infty}\frac{e^{-i k x}}{\sqrt{2 \pi}}\hat{a}^\dagger (k) dk\\
置 & \hat{a}(x) = \displaystyle \sum_k \frac{e^{i k x}}{\sqrt{L}} \hat{a}_k & \hat{a} (x) = \displaystyle \int^\infty_{-\infty} \frac{e^{i k x}}{\sqrt{2 \pi}} \hat{a}(k) dk\\
\hline
波 & \hat{a}^\dagger_k = \displaystyle \int^L_{x=0}\frac{e^{i k x}}{\sqrt{L}}\hat{a}^\dagger (x) dx & \hat{a}^\dagger_k = \displaystyle \int^\infty_{-\infty}\frac{e^{i k x}}{\sqrt{2 \pi}}\hat{a}^\dagger (x) dx \\
数 & \hat{a}_k = \displaystyle \int^L_{x=0} \frac{e^{-i k x}}{\sqrt{L}} \hat{a}(x) dx & \hat{a}_k = \displaystyle \int^\infty_{-\infty} \frac{e^{-i k x}}{\sqrt{2 \pi}} \hat{a}(x) dx \\
\hline
\end{array}

量子力学の世界を垣間見る(4):ボーズ粒子とフェルミ粒子

8.ボーズ粒子とフェルミ粒子

粒子には二つの種類がある。一つは、光子に代表されるボーズ粒子であり、他の一つは、電子を代表とするフェルミ粒子である。

ボーズ粒子が来るものは拒まずというような性質を有しているのに対して、フェルミ粒子は共存はできないという性格を持っている。

このような性質はどこから来るのであろうか。

今、二つの粒子\(a\),\(b\)があるとし、その位置を入れ替えることにしよう。入れ替える前の状態を\(|source>\)、後の状態を\(|target>\)で表すことにしよう。入れ替えるときの演算子を\(P\)とすると
\begin{eqnarray}
| target> = P | source>
\end{eqnarray}
となる。また、これを具体的に図で示すと
f:id:bitterharvest:20160912124028p:plain
なお、\(|source>=\hat{a}^\dagger_l \hat{a}^\dagger_k |0>\)で、\(|target>=\hat{a}^\dagger_k \hat{a}^\dagger_l |0>\)とする。即ち、\(a\)が発生演算子\(\hat{a}^\dagger_k\)によって格子点番号\(k\)に先に入り、\(b\)が\(\hat{a}^\dagger_l\)によって\(l\)に後で入って、状態\(|source>\)が作られたとする。さらに、\(a\)が\(\hat{a}^\dagger_l\)によって、\(l\)に先に入り、\(b\)が\(\hat{a}^\dagger_k\)によって\(k\)に後で入って、状態\(|target>\)が作られたとする。


また、\(|source>\)から\(|target>\)へは、次図のように、一つずつ粒子を移動させて得ることができる。
f:id:bitterharvest:20160912124058p:plain

この状態でもう一度入れ替えると、
\begin{eqnarray}
|source> = P |target> = P^2 |source>
\end{eqnarray}
となる。そこで、そこで上の式を満たす\(P\)は\(1\)あるいは\(-1\)となる。

これから、\(P\)の値によって二種類の粒子が存在することが分かるが、結論から言うと、\(P=1\)の場合がボーズ粒子であり、\(P=-1\)がフェルミ粒子である。そこで、この性質は、生成演算子同士、消滅演算子同士、あるいは生成演算子と消滅演算子の順序に由来していると考えてみよう。
そして、それぞれの粒子について、演算子の交換法則を定義してみよう。

8.1 ボーズ粒子

まず、ボーズ粒子から話を始めよう。\(P=1\)から演算子の順序を交換しても変わらないとし、次のことが成り立っていると考える。

二つの生成演算子\(\hat{a}^\dagger_k\)と\(\hat{a}^\dagger_l\)については、交換しても変わらないとする。即ち、
\begin{eqnarray}
\hat{a}^\dagger_k \hat{a}^\dagger_l= \hat{a}^\dagger_l \hat{a}^\dagger_k
\end{eqnarray}

二つの消滅演算子\(\hat{a}^\dagger_k\)と\(\hat{a}^\dagger_l\)についても、交換しても変わらないとする。
\begin{eqnarray}
\hat{a}^\dagger_k \hat{a}^\dagger_l = \hat{a}^\dagger_l \hat{a}^\dagger_k
\end{eqnarray}

消滅演算子\(\hat{a}_k\)と生成演算子\(\hat{a}^\dagger_l\)については、\(k \neq l\)の時は、交換しても変わらないとする。即ち、
\begin{eqnarray}
\hat{a}_k \hat{a}^\dagger_l = \hat{a}^\dagger_l \hat{a}_k
\end{eqnarray}

また、\(k = l\)の時は、生成させた後に消滅させることになる。これは何もしなかったことと同じなので、1ということにする。
\begin{eqnarray}
\hat{a}_k \hat{a}^\dagger_l= 1
\end{eqnarray}

そこで、
\begin{eqnarray}
\hat{a}^\dagger_l \hat{a}_l = 0
\end{eqnarray}
を考慮すると、
\begin{eqnarray}
\hat{a}_k \hat{a}^\dagger_l = \delta_{kl} + \hat{a}^\dagger_l \hat{a}_k
\end{eqnarray}
となる。なお、\(\delta_{kl}\)はδ関数で、\(k = l\)の時は\(1\)、\(k \neq l\)の時は\(0\)である。

交換カッコ\([\hat{A},\hat{B}]=\hat{A},\hat{B}-\hat{B},\hat{A}\)を用いると、上記の式は
\begin{eqnarray}
[\hat{a}^\dagger_k \hat{a}^\dagger_l] &=& \hat{a}^\dagger_l \hat{a}_l = 0 \\
[\hat{a}^\dagger_k \hat{a}^\dagger_l] &=& \hat{a}^\dagger_l \hat{a}^\dagger_k = 0 \\
[\hat{a}_k \hat{a}^\dagger_l] &=& \delta_{kl}
\end{eqnarray}
となる。

8.2 フェルミ粒子

次に、フェルミ粒子に移ろう。\(P=-1\)から順序を変えると変わってしまうと考え、次のことが成り立っていると考える。

二つの生成演算子\(\hat{c}^\dagger_k\)と\(\hat{c}^\dagger_l\)については、符号が変わるとする。即ち、
\begin{eqnarray}
\hat{c}^\dagger_k \hat{c}^\dagger_l= -\hat{c}^\dagger_l \hat{c}^\dagger_k
\end{eqnarray}

但し、\(k = l\)の時は、
\begin{eqnarray}
\hat{c}^\dagger_k \hat{c}^\dagger_k= -\hat{c}^\dagger_k \hat{c}^\dagger_k
\end{eqnarray}
となる。これを満足するのは、\(\hat{c}^\dagger_k \hat{c}^\dagger_k=0\)の時だけである。このことから、フェルミ粒子は、同じ場所では、複数が同時に存在できないということになる。

二つの消滅演算子\(\hat{c}^\dagger_k\)と\(\hat{c}^\dagger_l\)についても、符号が変わるとする。
\begin{eqnarray}
\hat{c}^\dagger_k \hat{c}^\dagger_l= -\hat{c}^\dagger_l \hat{c}^\dagger_k
\end{eqnarray}

消滅演算子\(\hat{c}_k\)と生成演算子\(\hat{c}^\dagger_l\)については、\(k \neq l\)の時は、符号が変わるとする。即ち、
\begin{eqnarray}
\hat{c}_k \hat{c}^\dagger_l= -\hat{c}^\dagger_l \hat{c}_k
\end{eqnarray}

また、\(k = l\)の時は、生成させた後に消滅させることになる。これは、ボーズ粒子の時と同様に、何もしなかったことと同じなので、1ということにする。
\begin{eqnarray}
\hat{c}_k \hat{c}^\dagger_l= 1
\end{eqnarray}

そこで、前と同じように、
\begin{eqnarray}
\hat{c}^\dagger_l \hat{c}_l = 0
\end{eqnarray}
を考慮すると、
\begin{eqnarray}
\hat{c}_k \hat{c}^\dagger_l = \delta_{kl} - \hat{c}^\dagger_l \hat{c}_k
\end{eqnarray}
となる。

反交換カッコ\({\hat{c},\hat{B}}=\hat{A},\hat{B}+\hat{B},\hat{c}\)を用いると、上記の式は
\begin{eqnarray}
{\hat{c}^\dagger_k \hat{c}^\dagger_l} &=& \hat{c}^\dagger_l \hat{c}_l = 0 \\
{\hat{c}^\dagger_k \hat{c}^\dagger_l} &=& \hat{c}^\dagger_l \hat{c}^\dagger_k = 0 \\
{\hat{c}_k \hat{c}^\dagger_l} &=& \delta_{kl}
\end{eqnarray}
となる。



8.3 Haskellで実装する

フェルミ粒子では、状態が符号を持つため、これまで説明してきたケットをそのまま用いてフェルミ粒子の状態を表すことはできない。ケットに符号の情報をつける必要があるので、符号とケットを対にして表すことにしよう。また、演算子は、生成演算子あるいは消滅演算子の並びで表すことにしよう。この二つのことを念頭に置いて、Haskellを用いて実装することにする。

上記の方針に基づいて、真空状態\(|0>\)への演算子、即ち、生成演算子と消滅演算子の並びを、配列で定義することにする。配列の要素は、生成か消滅かの区別と何番目の格子点であるかを示せるように対で表した。対の右側は、文字+,-でそれぞれ生成か消滅を表す。左側は、格子点の番号である。そこで、ボーズ粒子とフェルミ粒子それぞれに次のようなデータ型を用意した。

data Boson = Boson [(Char, Integer)] deriving (Show)

data Fermion = Fermion [(Char, Integer)] deriving (Show)

1)ボーズ粒子に関する実装

ボーズ粒子への演算子の作用は、先にケットを考えた時と同じであるが、記憶を呼び戻すために、図を用いて詳しく説明しよう。
f:id:bitterharvest:20160911165511p:plain

図は、配列で表された演算子を一つずつ順番に読み込んで、状態を作り出しているようすを示したものである。

①は、処理の途中を示したもので、演算子の配列はまだ処理していない演算子の並びを示したものである。符号は、1か-1である。フェルミ粒子はいずれかをとるが、ボーズ粒子の場合は常に1である。真空状態から始めて、いくつかの演算子の並びを作用させた結果、①のようになったと仮定し、ここを説明の出発点とする。

①では、現在の状態は、格子点の番号が1と2に粒子がある状態である。演算子の配列は右端から読み込む。従って、現在の状態は、配列の右側の要素(+ 3)で示される生成演算子\(\hat{a}^\dagger_3\)を作用させて、次の状態を得る。ボーズ粒子の場合は、生成演算子を作用させた場合には、いかなる状態においても、指定されている格子点番号に粒子を加えてよいので、②で示される状態になる。

②では、消滅演算子\(\hat{a}_2\)を作用させる。現在の状態の中に、この演算子で指定されている格子点の番号に粒子があれば、それを消去すればよい(この理由はすぐ後で説明する)。従って、③で示される状態になる。
なお、指定された場所に粒子がない場合には、状態はないものとする。即ち、0にする。

③では、生成演算子\(\hat{a}^\dagger_1\)を作用させる。生成演算子なので、これが指定しているところに粒子を追加し、④で示される状態になる。

④は、最終結果である。

ところで、②での処理は簡単に述べたが、実際には次の図で示すような行程により粒子が消去される。しかし、プログラムでは、効率を優先に、存在しているかいないかを判断して消去の処理を行う。
f:id:bitterharvest:20160911165634p:plain

それでは、プログラムを示そう。実際には、ケットでの生成演算子+^と消滅演算子-^の処理をそのまま用いる。

bose :: Boson -> (Integer, (Ket Integer))
bose (Boson a) = bose' (reverse a) (1, KetZero)

bose' [] b = b
bose' ((a, b):s) (c, k)
  | a == '+' = bose' s (c, b +^ k)
  | a == '-' = bose' s (c, b -^ k)
  | otherwise = error "unexpected error in bose operation."

発生演算子\(\hat{a}^\dagger_2\)と\(\hat{a}^\dagger_3\)を作用させてみる。

これは、配列では次のようになる。

a = [('+', 3), ('+', 2)]

これをボーズ粒子に作用する演算子の配列にすると、

*Test4> a
[('+',3),('+',2)]
*Test4> Boson a
Boson [('+',3),('+',2)]

となる。

この演算子を作用させると

*Test4> bose $ Boson a
(1,,a3+,a2+| ...00(0)00... >)

なお、上の結果は対になっていて、左側のa3+,a2+| ...00(0)00... >が状態を表し、右側の1が符号を表す。ボーズ粒子の場合には常に符号は1である。

プログラムが正しく動いているかどうかを確認してみよう。
\(\hat{a}^\dagger_2\)と\(\hat{a}^\dagger_3\)を作用させた後で、消滅演算子\(\hat{a}_3\)を作用させてみよう。

*Test4> b
[('-',3),('+',3),('+',2)]
*Test4> Boson b
Boson [('-',3),('+',3),('+',2)]
*Test4> bose $ Boson b
(1,,a2+| ...00(0)00... >)

\(\hat{a}^\dagger_2\)と\(\hat{a}^\dagger_3\)を作用させた後で、消滅演算子\(\hat{a}_2\)を作用させてみよう。

*Test4> c
[('-',2),('+',3),('+',2)]
*Test4> Boson c
Boson [('-',2),('+',3),('+',2)]
*Test4> bose $ Boson c
(1,,a3+| ...00(0)00... >)

\(\hat{a}^\dagger_2\)と\(\hat{a}^\dagger_3\)を作用させた後で、生成演算子\(\hat{a}^\dagger_2\)を作用させてみよう。

*Test4> d
[('+',2),('+',3),('+',2)]
*Test4> Boson d
Boson [('+',2),('+',3),('+',2)]
*Test4> bose $ Boson d
(1,,a2+,a3+,a2+| ...00(0)00... >)

上手く動いているようである。それでは、入れ替え操作を説明した図を参考にして、\(|source>=\hat{a}^\dagger_2 \hat{a}^\dagger_1 |0>\)から、粒子を入れ替えて\(|target>=\hat{a}^\dagger_1 \hat{a}^\dagger_2 |0>\)を作り出すことを考えよう。

これには、2番目の粒子を3番目に移し、次に1番目の粒子を2番目に移し、最後に3番目の粒子を1番目に移すとよいので、
\begin{eqnarray}
P= \hat{a}^\dagger_1 \hat{a}_3 \hat{a}^\dagger_2 \hat{a}_1 \hat{a}^\dagger_3 \hat{a}_2
\end{eqnarray}

となる。そこで、
\begin{eqnarray}
|target> = P \hat{a}^\dagger_2 \hat{a}^\dagger_1 |0>
\end{eqnarray}
が求めるものとなる。

そこで、演算子の配列は次のようになる。

*Test4> h
[('+',1),('-',3),('+',2),('-',1),('+',3),('-',2),('+',2),('+',1)]

これをボーズ粒子に作用する演算子の配列にすると、

*Test4> Boson h
Boson [('+',1),('-',3),('+',2),('-',1),('+',3),('-',2),('+',2),('+',1)]

となる。

この演算子を作用させると

*Test4> bose $ Boson h
(1,,a1+,a2+| ...00(0)00... >)

これより
\( |target> \) =(1,,a1+,a2+| ...00(0)00... >)
となる。また、
\( |source> \)=(1,,a2+,a1+| ...00(0)00... >)
であることから、\(|target>=|source>\)であることが分かり、\(P=1\)が確認できた。

2)フェルミ粒子に関する実装

フェルミ粒子への演算子の作用は、ボーズ粒子の場合と比べると少し複雑である。それらは以下の点による。
1) 演算子を交換すると符号が変わる。
2) 同じ格子点の番号には二つの粒子は同時に存在できない。

それでは、フェルミ粒子の処理を図を用いて詳しく説明しよう。
f:id:bitterharvest:20160911165705p:plain

図は、配列で表された演算子を一つずつ順番に読み込んで、状態を作り出しているようすを示したものである。

①は、生成演算子\(\hat{a}^\dagger_1\)を作用させる。ここでは、現在の状態の中に、この演算子で指定された場所に粒子が存在しているかどうか調べる。もし、存在していなければ、粒子を追加する。存在していれば、同じ場所に複数の粒子が入ることができないので、状態はないものとする。即ち、0にする。この場合には、格子点の番号1の場所には粒子が存在しないので、次の状態を得る。その結果、②となる。

②では、消滅演算子\(\hat{a}_2\)を作用させる。現在の状態の中に、この演算子で指定されている格子点の番号に粒子があれば、それを消去する。そうでない場合には、状態はないものとする。即ち、0にする。

ところで、消去する場合には、消去の対象となる粒子がどこにあるかで、状態の符号が変わる。これは次のようにして得る。この粒子の左隣に並ぶまでにいくつの粒子と交換しなければならないかを数える。図の場合には、消去しようとする粒子は格子点番号が2(a2+なので)である。このためには、格子点番号3(a3+なので)の粒子と交換しなければならないので、交換数は1である。交換数が偶数の場合には符号はそのまま、奇数の場合には符号を反転させる。この場合は、符号は反転させることになるので、-1となる。そして、③となる。

③では、生成演算子\(\hat{a}^\dagger_1\)を作用させる。生成演算子なので、この場所に粒子が存在しているかどうかを調べる。存在しているので、状態はないものとする。即ち、0にする。

④は、最終結果である。

ところで、②での処理は簡単に述べたが、実際には次の図で示すような行程により粒子が消去される。粒子が並んだ後、これらの粒子を最も右側に位置するように移動する。
f:id:bitterharvest:20160911165740p:plain

この時、どちらの粒子も同じ交換数となる。従って、並んだ後、右側に移動させたときの交換数は、両者の交換数の合計となるので、必ず、偶数となる。従って、符号がどのように変わるかを調べるためには、隣に並ぶまでの交換数を求めれば十分である。

それでは、プログラムを示そう。singleは、発生演算子が指定した場所に粒子を加えた場合、単独なのか複数になるのかを調べる関数である。deleteは、消滅演算子が指定した場所から粒子を取り去る関数である。signは隣に並ばせるまでの交換数を調べる関数である。

fermi :: Fermion -> (Integer, (Ket Integer))
fermi (Fermion a) = fermi' (reverse a) (1, KetZero)

fermi' [] b = b
fermi' ((a, b):s) (c, k)
      | a == '+' = fermi' s (fg c b k)
      | a == '-' = fermi' s (fd c b k)
      | otherwise = error "unexpected error in fermi operation."
  where
    fg m a b
      | single a b = (m, a +^b)
      | otherwise = (m * (-1) ^ n, Zero)
    fg' _ Zero = Zero
    single a KetZero = True
    single a (x :+: xs) 
      | a == x = False 
      | otherwise = single a xs        
    fd m a b
      | b == p = (m, Zero)
      | otherwise = (m * (-1) ^ n, p) 
    p = delete b k
    delete a KetZero = KetZero
    delete a (x :+: xs) 
      | a == x = xs 
      | otherwise = x :+: delete a xs
    n = sign b k
    sign a KetZero = 0
    sign a (x :+: xs)
      | a == x = 0
      | otherwise = 1 + sign a xs

ボーズ粒子の時と同じように、発生演算子\(\hat{c}^\dagger_2\)と\(\hat{c}^\dagger_3\)を作用させてみる。

これは、前と同じで、配列では次のようになる。

a = [('+', 3), ('+', 2)]

これをフェルミ粒子に作用する演算子の配列にすると、

*Test4> a
[('+',3),('+',2)]
*Test4> Fermion a
Fermion [('+',3),('+',2)]

となる。

この演算子を作用させると

*Test4> fermi $ Fermion a
(1,,a3+,a2+| ...00(0)00... >

なお、上の結果は、ボーズ粒子の時と同じように、対になっていて、左側のa3+,a2+| ...00(0)00... >が状態を表し、右側の1が符号を表す。

プログラムが正しく動いているかどうかを確認してみよう。
\(\hat{c}^\dagger_2\)と\(\hat{c}^\dagger_3\)を作用させた後で、消滅演算子\(\hat{c}_3\)を作用させてみよう。

*Test4> b
[('-',3),('+',3),('+',2)]
*Test4> Fermion b
Fermion [('-',3),('+',3),('+',2)]
*Test4> fermi $ Fermion b
(1,,a2+| ...00(0)00... >)

\(\hat{c}^\dagger_2\)と\(\hat{c}^\dagger_3\)を作用させた後で、消滅演算子\(\hat{c}_2\)を作用させてみよう。

*Test4> c
[('-',2),('+',3),('+',2)]
*Test4> Fermion c
Fermion [('-',2),('+',3),('+',2)]
*Test4> fermi $ Fermion c
(-1,,a3+| ...00(0)00... >)

\(\hat{c}^\dagger_2\)と\(\hat{c}^\dagger_3\)を作用させた後で、生成演算子\(\hat{c}^\dagger_2\)を作用させてみよう。

*Test4> d
[('+',2),('+',3),('+',2)]
*Test4> Fermion d
Fermion [('+',2),('+',3),('+',2)]
*Test4> fermi $ Fermion d
(-1,0)

上手く動いているようである。それではボーズの時と同様に、\(|source>=\hat{c}^\dagger_2 \hat{c}^\dagger_1 |0>\)から、粒子を入れ替えて\(|target>=\hat{c}^\dagger_1 \hat{c}^\dagger_2 |0>\)を作り出すことを考えよう。

それはボーズ粒子の時と同じで、2番目の粒子を3番目に移し、次に1番目の粒子を2番目に移し、最後に3番目の粒子を1番目に移すとよい。従って、
\begin{eqnarray}
P= \hat{c}^\dagger_1 \hat{c}_3 \hat{c}^\dagger_2 \hat{c}_1 \hat{c}^\dagger_3 \hat{c}_2
\end{eqnarray}

となる。そこで、
\begin{eqnarray}
|target> = -P \hat{c}^\dagger_2 \hat{c}^\dagger_1 |0>
\end{eqnarray}
が求めるものとなる。

そこで、演算子の配列は次のようになる。

*Test4> h
[('+',1),('-',3),('+',2),('-',1),('+',3),('-',2),('+',2),('+',1)]

これをフェルミ粒子に作用する演算子の配列にすると、

*Test4> Fermion h
Fermion [('+',1),('-',3),('+',2),('-',1),('+',3),('-',2),('+',2),('+',1)]

となる。

この演算子を作用させると

*Test4> fermi $ Fermion h
(1,,a1+,a2+| ...00(0)00... >)

これより
\( |target> \) =(1,,a1+,a2+| ...00(0)00... >)
となる。また、
\( |source> \) =(1,,a2+,a1+| ...00(0)00... >)
であることから、\(| target>=-|source>\)であることが分かり、\(P=-1\)が確認できた。

注意:なお、\(\hat{c}^\dagger_2,\hat{c}_l\)はプログラムではcl+,cl-ではなくal+,al-となっているので、注意すること。なお、気になるようであれば、プログラムを修正すること。

8.4 プログラムの全体

プログラムの全体を示す。

module Qtm.BoseFermi.BoseFermi (Boson (Boson), Fermion (Fermion), bose, fermi) where
import Qtm.KetBra.Ket

data Boson = Boson [(Char, Integer)] deriving (Show)

data Fermion = Fermion [(Char, Integer)] deriving (Show)

bose :: Boson -> (Integer, (Ket Integer))
bose (Boson a) = bose' (reverse a) (1, KetZero)

bose' [] b = b
bose' ((a, b):s) (c, k)
  | a == '+' = bose' s (c, b +^ k)
  | a == '-' = bose' s (c, b -^ k)
  | otherwise = error "unexpected error in bose operation."

fermi :: Fermion -> (Integer, (Ket Integer))
fermi (Fermion a) = fermi' (reverse a) (1, KetZero)

fermi' [] b = b
fermi' ((a, b):s) (c, k)
      | a == '+' = fermi' s (fg c b k)
      | a == '-' = fermi' s (fd c b k)
      | otherwise = error "unexpected error in fermi operation."
  where
    fg m a b
      | single a b = (m, a +^b)
      | otherwise = (m * (-1) ^ n, Zero)
    fg' _ Zero = Zero
    single a KetZero = True
    single a (x :+: xs) 
      | a == x = False 
      | otherwise = single a xs        
    fd m a b
      | b == p = (m, Zero)
      | otherwise = (m * (-1) ^ n, p) 
    p = delete b k
    delete a KetZero = KetZero
    delete a (x :+: xs) 
      | a == x = xs 
      | otherwise = x :+: delete a xs
    n = sign b k
    sign a KetZero = 0
    sign a (x :+: xs)
      | a == x = 0
      | otherwise = 1 + sign a xs

なおテストに用いたプログラムは以下の通りである。

module Test4 where

import Qtm.KetBra
import Qtm.BoseFermi.BoseFermi

a = [('+', 3), ('+', 2)]

b = [('-', 3), ('+', 3), ('+', 2)]

c = [('-', 2), ('+', 3), ('+', 2)]

d = [('+', 2), ('+', 3), ('+', 2)]

e = [('+',3), ('-',2), ('+',2), ('+',1)]

f = [('-',1), ('+',3), ('-',2), ('+',2), ('+',1)]

g = [('-',3), ('+',2), ('-',1), ('+',3), ('-',2), ('+',2), ('+',1)]

h = [('+',1), ('-',3), ('+',2), ('-',1), ('+',3), ('-',2), ('+',2), ('+',1)]

トーハクで「藤原行成の書」を鑑賞する

東京国立博物館に出かけてみた。
f:id:bitterharvest:20160903161325j:plain

初めてではないのだが、入館した記憶は1965年のツタンカーメン展だけだ。もしこれが正しければなんと51年ぶりになる。『黄金のマスク』を見るために、ずいぶんと長い時間並んだことを覚えている。

週の初めに、藤原行成(こうぜい)の書が展示されていると教えて頂いた。特に、『陣定定文案(じんていさだめぶんあん)』を見逃してはいけないということであった。

藤原行成(972-1028)のことを簡単に紹介しておこう。彼は、平安中期を、藤原道長(966-1028)とともに同時代を生きた人だ(なんと二人は同じ日に亡くなった)。

このころは摂政関白時代が頂点に達した時で、道長は、長女の彰子(しょうし)を一条天皇の、次女の妍子(けんし)を三条天皇の、四女の威子(いし)を後一条天皇の中宮にし、「一家立三后」を成し遂げた。

この時代のことを彼らは日記に残している、行成は『権記』に、道長は『御堂関白記』に、そしてもう一人、藤原実資(957-1046)は『小右記』に記しているので、この当時の貴族がどのような生活をしていたかが分かる。なお、御堂関白記は現存する最古の日記としてユネスコ記録遺産に登録されている。

話を元に戻そう。行成は、当代の能書家として三蹟の一人に数えられている。今回は、行成の書と彼の書風を真似た人たちの書の展示である。行成の書として、最初に展示されていたのは、国宝『白氏誌巻』である(写真は東京国立博物館ホームページよりの複写)。
f:id:bitterharvest:20160903170541j:plain
「経師」の筆を借りて書いたので、普段とは違っており、笑わないで欲しいと注釈に書かれているとのこと。いつもの筆であればこれにも増して素晴らしいのであろう。自信作を見る機会がないのが残念である。

次に展示されていたのが、お目当ての『陣定定文案』である。

陣定は、天皇から諮問のあった事項に対して、大臣以下の公卿と四位の参議以上の議政官が論じ、見解(一致しない場合には複数)を天皇に奏上する諮問機関である。

この書は奏上するための文案を書いたものなので、行成の普段の字だろうから是非見ておくべきだいうのが、この展示を紹介してくれた方の推奨理由であった。残念ながら東京国立博物館のホームページにこの書は掲載されていないので、お見せすることはできない。展示のパンフレットを購入したので手元では見ることができるが、急いで書いているにもかかわらず立派な字である。

三番目は『書状』であった。昇進したのにお祝いに来てくれないことを責めている手紙である。手紙の最初の文字が草名(花押の一種)になっているのが面白かった(写真は東京国立博物館ホームページよりの複写)。
f:id:bitterharvest:20160903213312j:plain

2011年に『源氏物語 千年の謎』という映画があった。藤原道長紫式部が関係があったという想定のもとに、式部自身の宮廷生活と源氏物語を同時に進行させたものだ。この映画の中で、いつも書籍を読んでいる学者肌の貴族が甲本雅裕演じる藤原行成である。
f:id:bitterharvest:20160903213754j:plain

展示は一部屋であったので、30分ぐらい見た後、埴輪から浮世絵までが展示されている常設展を見て本館を後にした。

本館前の風景は
f:id:bitterharvest:20160903174700j:plain

この日は暑く、人影も少ない。門の左側に位置する表慶館
f:id:bitterharvest:20160903175121j:plain
このあたりにも人影は見られない。

だいぶ暑かったが、恩賜公園を少し散策してみた。的屋さんの屋台とはずいぶんと異なるかわいらしい屋台がいくつも並んでいて、かなりの人ごみになっている一角があった。
後で調べたのだが、芸大の学生がアートマーケットをしていたとのことである。
f:id:bitterharvest:20160903175558j:plain

アートマーケットを通り過ぎて、少し行ったところに、子供のころによく訪れた上野動物園の入り口があった。
f:id:bitterharvest:20160903175758j:plain
当時とはずいぶんと変わっているのだろうと思ったが、暑さには勝てず、そのまま通り過ぎた。

しばらく行くと、上野東照宮が修復なったという掲示があった。前に来たときは、修復中だったので、どのように変わったのかを確認したくて、入り口まで行ってみる。
f:id:bitterharvest:20160903180202j:plain
金ぴかな門になっていた。中に入って参拝するほどの興味はわかなかったので、上野駅の方に向かう。

かつては、東北、信越地方への玄関口であった上野駅中央口の様子を眺めて、
f:id:bitterharvest:20160903180416j:plain

地下鉄を利用して、帰路についた。

量子力学の世界を垣間見る(3):粒子が相互に交換されるモデル:周期系

6.周期系

量子力学の世界をHaskellで覗けるようにするために、格子点の数が有限である場合を前回の記事で取り上げた。その時は端があるモデルについて考えたが、この記事では端同士がつながって輪のようになっている有限系について考えてみよう。これを周期系と呼ぶことにしよう。周期系のモデルを図示すると以下の様である。
f:id:bitterharvest:20160827104401p:plain

そこで、このモデルのハミルトニアンを\(\hat{H}_{periodic}\)とすると、
\begin{eqnarray}
\hat{H}_{periodic}=-g\sum_{l=0}^{N-1} (\hat{a}^\dagger_{l+1} \hat{a}_l + \hat{a}^\dagger_{l} \hat{a}_{l+1})
\end{eqnarray}
となる。なお、\(l=N\)の時、\(l=0\)である。

\(\hat{H}_{periodic}\)の固有状態は平面波状態
\begin{eqnarray}
\sum_{l=0}^{N-1} \varphi_{kl} \hat{a}^\dagger_l |0> = \sum_{l=0}^{N-1} Ae^{i \kappa l} \hat{a}^\dagger_l |0>
\end{eqnarray}
となる。但し、\(e^{i \kappa N} = 1\)である。即ち、\(\kappa N=2 \pi m\)である。

これより、規格化された固有状態は
\begin{eqnarray}
&&| \kappa > \\
&=& \sum_{l=0}^{N-1} \varphi_{kl} \hat{a}^\dagger_l |0> \\
&=& \sum_{l=0}^{N-1} \frac{1}{\sqrt N} e^{i \kappa l} |0>
\end{eqnarray}

これをブラで表すと、
\begin{eqnarray}
&&< \kappa | \\
&=& \sum_{l=0}^{N-1} <0| \hat{a}_l \varphi^*_{kl} \\
&=& \sum_{l=0}^{N-1} <0| \hat{a}_l \frac{1}{\sqrt N} e^{-i \kappa l}
\end{eqnarray}
となる。

7.Haskellでの表現

周期系の場合にも、前回と同様、固有状態を表示することができる。そこで、実現してみよう。
格子点lに粒子がある状態lattice m lを、規格化せれた割合との対として、次のように表すことにしよう。なお、説明のところで用いた区切りの数\(N\)はプログラムではnである。

lattice :: Integer -> Integer -> (Complex Double, Integer)
lattice m l = (b * cos theta  :+ b * sin theta, l)
  where
    theta = 2 * pi * fromInteger m * fromInteger l / fromInteger n

これを用いて、周期系での重ね合わせでの固有状態kappa mはケットでは次のようになる。なお、ここで、mは1からnまでの整数である。

kappa :: Integer -> Entangle (Complex Double) Integer
kappa m 
  | m < 1  = error $ "m should be 1 <= m <= " ++ show n ++ "."
  | m > 10 = error $ "m should be 1 <= m <= " ++ show n ++ "."
  | otherwise = kappa1 m (n - 1)
  where
    kappa1 m 0 = lattice m 0 :&: EnZero
    kappa1 m l = lattice m l :&: kappa1 m (l - 1)

そこで、\(N=10\)の場合について、詳しく調べてみよう。プログラムは次のようになる。

{-#LANGUAGE GADTs #-}

module Test3 where

import Data.Complex
import Qtm.KetBra
import Qtm.Entangle

n = 10
b = 1.0 / sqrt (fromInteger n)

lattice :: Integer -> Integer -> (Complex Double, Integer)
lattice m l = (b * cos theta  :+ b * sin theta, l)
  where
    theta = 2 * pi * fromInteger m * fromInteger l / fromInteger n

kappa :: Integer -> Entangle (Complex Double) Integer
kappa m 
  | m < 1  = error $ "m should be 1 <= m <= " ++ show n ++ "."
  | m > 10 = error $ "m should be 1 <= m <= " ++ show n ++ "."
  | otherwise = kappa1 m (n - 1)
  where
    kappa1 m 0 = lattice m 0 :&: EnZero
    kappa1 m l = lattice m l :&: kappa1 m (l - 1)

このプログラムをロードして、mの値を変えて、固有状態を見てみよう。

relude> :load "test3.hs"
[1 of 7] Compiling Qtm.KetBra.Ket   ( Qtm\KetBra\Ket.hs, interpreted )
[2 of 7] Compiling Qtm.KetBra.Bra   ( Qtm\KetBra\Bra.hs, interpreted )
[3 of 7] Compiling Qtm.Entangle.BraEntangle ( Qtm\Entangle\BraEntangle.hs, interpreted )
[4 of 7] Compiling Qtm.Entangle.KetEntangle ( Qtm\Entangle\KetEntangle.hs, interpreted )
[5 of 7] Compiling Qtm.Entangle     ( Qtm\Entangle.hs, interpreted )
[6 of 7] Compiling Qtm.KetBra       ( Qtm\KetBra.hs, interpreted )
[7 of 7] Compiling Test3            ( test3.hs, interpreted )
Ok, modules loaded: Qtm.KetBra, Qtm.Entangle, Qtm.Entangle.KetEntangle, Qtm.Entangle.BraEntangle, Qtm.KetBra.Bra, Qtm.KetBra.Ket, Test3.

m=1の場合は次のようになる。

*Test3> kappa 1
0.25583363680084636 :+ (-0.1858740172300923)_a9^ + 9.771975379242734e-2 :+ (-0.3007504775037729)_a8^ + (-9.771975379242745e-2) :+ (-0.3007504775037728)_a7^ + (-0.2558336368008464) :+ (-0.18587401723009223)_a6^ + (-0.31622776601683794) :+ 3.872545315192758e-17_a5^ + (-0.25583363680084636) :+ 0.18587401723009228_a4^ + (-9.771975379242738e-2) :+ 0.3007504775037729_a3^ + 9.771975379242741e-2 :+ 0.3007504775037728_a2^ + 0.25583363680084636 :+ 0.18587401723009225_a1^ + 0.31622776601683794 :+ 0.0_a0^

上手く動いているようなので、その他の場合も求めてみよう。

*Test3> kappa 2
9.771975379242727e-2 :+ (-0.30075047750377293)_a9^ + (-0.2558336368008465) :+ (-0.18587401723009214)_a8^ + (-0.2558336368008463) :+ 0.1858740172300923_a7^ + 9.771975379242749e-2 :+ 0.3007504775037728_a6^ + 0.31622776601683794 :+ (-7.745090630385516e-17)_a5^ + 9.771975379242734e-2 :+ (-0.3007504775037729)_a4^ + (-0.2558336368008464) :+ (-0.18587401723009223)_a3^ + (-0.25583363680084636) :+ 0.18587401723009228_a2^ + 9.771975379242741e-2 :+ 0.3007504775037728_a1^ + 0.31622776601683794 :+ 0.0_a0^
*Test3> kappa 3
(-9.77197537924276e-2) :+ (-0.3007504775037728)_a9^ + (-0.25583363680084625) :+ 0.1858740172300924_a8^ + 0.2558336368008465 :+ 0.18587401723009211_a7^ + 9.771975379242727e-2 :+ (-0.30075047750377293)_a6^ + (-0.31622776601683794) :+ 1.1617635945578272e-16_a5^ + 9.771975379242749e-2 :+ 0.3007504775037728_a4^ + 0.25583363680084636 :+ (-0.1858740172300923)_a3^ + (-0.2558336368008464) :+ (-0.18587401723009223)_a2^ + (-9.771975379242738e-2) :+ 0.3007504775037729_a1^ + 0.31622776601683794 :+ 0.0_a0^
*Test3> kappa 4
(-0.25583363680084653) :+ (-0.18587401723009203)_a9^ + 9.771975379242764e-2 :+ 0.30075047750377276_a8^ + 9.77197537924272e-2 :+ (-0.30075047750377293)_a7^ + (-0.25583363680084625) :+ 0.1858740172300924_a6^ + 0.31622776601683794 :+ (-1.549018126077103e-16)_a5^ + (-0.2558336368008465) :+ (-0.18587401723009214)_a4^ + 9.771975379242749e-2 :+ 0.3007504775037728_a3^ + 9.771975379242734e-2 :+ (-0.3007504775037729)_a2^ + (-0.25583363680084636) :+ 0.18587401723009228_a1^ + 0.31622776601683794 :+ 0.0_a0^
*Test3> kappa 5
(-0.31622776601683794) :+ 3.485290783673482e-16_a9^ + 0.31622776601683794 :+ (-3.098036252154206e-16)_a8^ + (-0.31622776601683794) :+ 2.7107817206349303e-16_a7^ + 0.31622776601683794 :+ (-2.3235271891156544e-16)_a6^ + (-0.31622776601683794) :+ 1.9362726575963788e-16_a5^ + 0.31622776601683794 :+ (-1.549018126077103e-16)_a4^ + (-0.31622776601683794) :+ 1.1617635945578272e-16_a3^ + 0.31622776601683794 :+ (-7.745090630385516e-17)_a2^ + (-0.31622776601683794) :+ 3.872545315192758e-17_a1^ + 0.31622776601683794 :+ 0.0_a0^
*Test3> kappa 6
(-0.25583363680084614) :+ 0.18587401723009261_a9^ + 9.771975379242705e-2 :+ (-0.300750477503773)_a8^ + 9.771975379242771e-2 :+ 0.30075047750377276_a7^ + (-0.25583363680084653) :+ (-0.18587401723009203)_a6^ + 0.31622776601683794 :+ (-2.3235271891156544e-16)_a5^ + (-0.25583363680084625) :+ 0.1858740172300924_a4^ + 9.771975379242727e-2 :+ (-0.30075047750377293)_a3^ + 9.771975379242749e-2 :+ 0.3007504775037728_a2^ + (-0.2558336368008464) :+ (-0.18587401723009223)_a1^ + 0.31622776601683794 :+ 0.0_a0^
*Test3> kappa 7
(-9.7719753792428e-2) :+ 0.30075047750377265_a9^ + (-0.25583363680084664) :+ (-0.1858740172300919)_a8^ + 0.2558336368008455 :+ (-0.18587401723009347)_a7^ + 9.771975379242771e-2 :+ 0.30075047750377276_a6^ + (-0.31622776601683794) :+ 2.7107817206349303e-16_a5^ + 9.77197537924272e-2 :+ (-0.30075047750377293)_a4^ + 0.2558336368008465 :+ 0.18587401723009211_a3^ + (-0.2558336368008463) :+ 0.1858740172300923_a2^ + (-9.771975379242745e-2) :+ (-0.3007504775037728)_a1^ + 0.31622776601683794 :+ 0.0_a0^
*Test3> kappa 8
9.771975379242794e-2 :+ 0.30075047750377265_a9^ + (-0.2558336368008461) :+ 0.18587401723009264_a8^ + (-0.25583363680084664) :+ (-0.1858740172300919)_a7^ + 9.771975379242705e-2 :+ (-0.300750477503773)_a6^ + 0.31622776601683794 :+ (-3.098036252154206e-16)_a5^ + 9.771975379242764e-2 :+ 0.30075047750377276_a4^ + (-0.25583363680084625) :+ 0.1858740172300924_a3^ + (-0.2558336368008465) :+ (-0.18587401723009214)_a2^ + 9.771975379242734e-2 :+ (-0.3007504775037729)_a1^ + 0.31622776601683794 :+ 0.0_a0^
*Test3> kappa 9
0.2558336368008474 :+ 0.18587401723009084_a9^ + 9.771975379242794e-2 :+ 0.30075047750377265_a8^ + (-9.7719753792428e-2) :+ 0.30075047750377265_a7^ + (-0.25583363680084614) :+ 0.18587401723009261_a6^ + (-0.31622776601683794) :+ 3.485290783673482e-16_a5^ + (-0.25583363680084653) :+ (-0.18587401723009203)_a4^ + (-9.77197537924276e-2) :+ (-0.3007504775037728)_a3^ + 9.771975379242727e-2 :+ (-0.30075047750377293)_a2^ + 0.25583363680084636 :+ (-0.1858740172300923)_a1^ + 0.31622776601683794 :+ 0.0_a0^
*Test3> kappa 10
0.31622776601683794 :+ (-6.970581567346964e-16)_a9^ + 0.31622776601683794 :+ (-6.196072504308412e-16)_a8^ + 0.31622776601683794 :+ (-5.421563441269861e-16)_a7^ + 0.31622776601683794 :+ (-4.647054378231309e-16)_a6^ + 0.31622776601683794 :+ (-3.8725453151927575e-16)_a5^ + 0.31622776601683794 :+ (-3.098036252154206e-16)_a4^ + 0.31622776601683794 :+ (-2.3235271891156544e-16)_a3^ + 0.31622776601683794 :+ (-1.549018126077103e-16)_a2^ + 0.31622776601683794 :+ (-7.745090630385516e-17)_a1^ + 0.31622776601683794 :+ 0.0_a0^

1より小さい値、あるいは、10より大きい値を指定した場合は次のようになる。

*Test3> kappa 0
*** Exception: m should be 1 <= m <= 10.
*Test3> kappa 11
*** Exception: m should be 1 <= m <= 10.

そこで、m=1,2,3の固有状態の実数部を図で表すと次のようになる。
f:id:bitterharvest:20160828113237p:plain
ついでに、ブラでの表現も加えて、プログラムを次のようにしよう。ここで、格子点lに粒子がある状態をlattice' m lで、ブラでの固有状態をkappa' mで表す。

{-#LANGUAGE GADTs #-}

module Test3 where

import Data.Complex
import Qtm.KetBra
import Qtm.Entangle

n = 10
b = 1.0 / sqrt (fromInteger n)

lattice :: Integer -> Integer -> (Complex Double, Integer)
lattice m l = (b * cos theta  :+ b * sin theta, l)
  where
    theta = 2 * pi * fromInteger m * fromInteger l / fromInteger n

kappa :: Integer -> Entangle (Complex Double) Integer
kappa m 
  | m < 1  = error $ "m should be 1 <= m <= " ++ show n ++ "."
  | m > 10 = error $ "m should be 1 <= m <= " ++ show n ++ "."
  | otherwise = kappa1 m (n - 1)
  where
    kappa1 m 0 = lattice m 0 :&: EnZero
    kappa1 m l = lattice m l :&: kappa1 m (l - 1)

lattice' :: Integer -> Integer -> (Integer, Complex Double)
lattice' m l = (l, b * cos theta  :+ (- b * sin theta))
  where
    theta = 2 * pi * fromInteger m * fromInteger l / fromInteger n

kappa' :: Integer -> CoEntangle Integer (Complex Double)
kappa' m 
  | m < 1  = error $ "m should be 1 <= m <= " ++ show n ++ "."
  | m > 10 = error $ "m should be 1 <= m <= " ++ show n ++ "."
  | otherwise = kappa1 m (n - 1)
  where
    kappa1 m 0 = EnZero' :|: lattice' m 0
    kappa1 m l = kappa1 m (l - 1) :|: lattice' m l

プログラムをロードして、ブラでの固有状態を調べてみよう。

*Test3> kappa' 1
a0_0.31622776601683794 :+ (-0.0) + a1_0.25583363680084636 :+ (-0.18587401723009225) + a2_9.771975379242741e-2 :+ (-0.3007504775037728) + a3_(-9.771975379242738e-2) :+ (-0.3007504775037729) + a4_(-0.25583363680084636) :+ (-0.18587401723009228) + a5_(-0.31622776601683794) :+ (-3.872545315192758e-17) + a6_(-0.2558336368008464) :+ 0.18587401723009223 + a7_(-9.771975379242745e-2) :+ 0.3007504775037728 + a8_9.771975379242734e-2 :+ 0.3007504775037729 + a9_0.25583363680084636 :+ 0.1858740172300923
*Test3> kappa' 2
a0_0.31622776601683794 :+ (-0.0) + a1_9.771975379242741e-2 :+ (-0.3007504775037728) + a2_(-0.25583363680084636) :+ (-0.18587401723009228) + a3_(-0.2558336368008464) :+ 0.18587401723009223 + a4_9.771975379242734e-2 :+ 0.3007504775037729 + a5_0.31622776601683794 :+ 7.745090630385516e-17 + a6_9.771975379242749e-2 :+ (-0.3007504775037728) + a7_(-0.2558336368008463) :+ (-0.1858740172300923) + a8_(-0.2558336368008465) :+ 0.18587401723009214 + a9_9.771975379242727e-2 :+ 0.30075047750377293


また、ケットとブラの内積も求めて見よう。

*Test3> kappa' 1 ^**^ kappa 1
1.0 :+ 0.0
*Test3> kappa' 2 ^**^ kappa 2
1.0 :+ 0.0

またブラとケットでmの値が異なるときは、

*Test3> kappa' 1 ^**^ kappa 2
(-5.551115123125783e-17) :+ (-2.7755575615628914e-17)
*Test3> kappa' 2 ^**^ kappa 3
(-5.551115123125783e-17) :+ 0.0
*Test3> kappa' 2 ^**^ kappa 4
(-3.122502256758253e-17) :+ 0.0

理論的には0であるが、実際は誤差のため0にはならない。しかし、0に限りなく近い値を結果として得る。