====== 概要 ======
もっとも簡単な例として、シングルサイトにおけるスピン演算子と波動関数を定義する。
====== 理論 ======
スピン1/2を持った1つの電子を考える。
基底となる状態はスピンがアップかダウンとなる2通りが考えられる。
アップとダウンのの状態を、それぞれ $| 1 \rangle$ と $| 2 \rangle$ で表すことにする。
ベクトルで書くと
\begin{equation}
| 1 \rangle =
\begin{pmatrix}
1 \\ 0
\end{pmatrix}
, \qquad
| 2 \rangle =
\begin{pmatrix}
0 \\ 1
\end{pmatrix}
\end{equation}
となる。
このとき、系の状態はこれら基底の線形結合
\begin{equation}
| \psi \rangle = \sum_{s=1}^{2} \psi_s | s \rangle
= \begin{pmatrix}
\psi_1 \\ \psi_2
\end{pmatrix}
\end{equation}
で表すことができる。
ここで $\psi_s$ は状態を表すベクトルの$s$番目の成分である。
スピン演算子の$x$成分および$z$成分を考える。
行列で書くと
\begin{equation}
S^x =
\begin{pmatrix}
0 & 1/2 \\
1/2 & 0
\end{pmatrix}
, \qquad
S^z =
\begin{pmatrix}
1/2 & 0 \\
0 & -1/2
\end{pmatrix}
\end{equation}
となる。
たとえばある状態にスピン演算子の$x$成分$S^x$を作用させれば
\begin{equation}
S^x | \psi \rangle = \frac{1}{2}\psi_1 | 2 \rangle + \frac{1}{2} \psi_2 | 1 \rangle
\end{equation}
となる。
状態$| \psi \rangle$におけるスピン演算子の期待値は
\begin{equation}
\langle \psi | S^x | \psi \rangle = \sum_{s, s'} \psi^*_s S^x_{s, s'} \psi_{s'}
\end{equation}
となる。
====== ソースコード ======
ITensorをインストールしたディレクトリ内にある/tutorial/01_one_site/のソースコードを参照。
#include "itensor/all.h"
using namespace itensor;
int main()
{
//
// Single-site wavefunction
//
//Make a dimension 2 Index
auto s = Index("s",2);
//Construct an ITensor
auto psi = ITensor(s); //default initialized to zero
//
// Initialize up spin
//
//Set first element to 1.
psi.set(s(1),1);
PrintData(psi);
//
// Operators
//
auto Sz = ITensor(s,prime(s));
auto Sx = ITensor(s,prime(s));
Sz.set(s(1),prime(s)(1),+0.5);
Sz.set(s(2),prime(s)(2),-0.5);
Sx.set(s(1),prime(s)(2),+0.5);
Sx.set(s(2),prime(s)(1),+0.5);
PrintData(Sz);
PrintData(Sx);
//
// Product Sx * phi
//
ITensor phi = Sx * psi;
phi.noprime();
PrintData(phi);
//
// 45* angle spin
//
Real theta = Pi/4;
//Extra factors of two come from S=1/2 representation
psi.set(s(1),cos(theta/2));
psi.set(s(2),sin(theta/2));
PrintData(psi);
//
// Expectation values
//
auto cpsi = dag(prime(psi));
Real zz = (cpsi * Sz * psi).real();
Real xx = (cpsi * Sx * psi).real();
println(" = ", zz);
println(" = ", xx);
return 0;
}
====== プログラムの解説 ======
テンソルネットワーク理論では、演算子や状態がテンソルあるいはテンソル積で書けることを使う。
求めたい演算子や状態に対応するテンソルを定義していくことで計算を進める。
テンソルの定義やテンソル同士の演算に便利なクラスが、ITensorではライブラリとして提供されている。
===== ライブラリのインクルード =====
#include "itensor/all.h"
ITensorのライブラリをインクルードする。
all.hはITensorのすべてのクラスがインクルードされるが、プログラムが重くなったりコンパイルに時間がかかったりするので、慣れてくれば必要なヘッダファイルだけインクルードすればよい。
===== テンソルの添字の定義 =====
テンソルの添字$s$を定義する。
auto s = Index("s",2);
クラスIndexにより、添字オブジェクトsを定義する。
書き方は Index("[添字に使う記号]", [添字の次元]) である。
今はアップとダウンからなるので、添え字の次元は2である。
===== テンソルの定義 ======
添え字の定義ができたので、状態と演算子を構成する。
状態$\psi_s$を次のように作成する。
auto psi = ITensor(s); //default initialized to zero
ITensor(s)によりオブジェクトsで定義された添字をもつテンソルのオブジェクトを作成する。
テンソルの成分はすべてゼロに初期化されている。
値を設定するには、例えば$\psi_1 = 1$としたい場合
psi.set(s(1),1);
とすればよい。
このようにメソッド .set によってテンソルの成分を変更できる。
できあがったテンソルの中身を表示したい場合は PrintDataを使えばよい。
PrintData(psi);
次にスピン演算子$S^z_{s,s'}$および$S^x_{s, s'}$を作成する。
2階のテンソルを作りたい場合は次のようにする。
auto Sz = ITensor(s,prime(s));
auto Sx = ITensor(s,prime(s));
prime(s) によって次元が添字$s$と同じである添字$s'$を生成する。
このままだと演算子のすべての成分がゼロのままなので、値を設定する。
Sz.set(s(1),prime(s)(1),+0.5);
Sz.set(s(2),prime(s)(2),-0.5);
Sx.set(s(1),prime(s)(2),+0.5);
Sx.set(s(2),prime(s)(1),+0.5);
===== テンソル同士の積 =====
テンソル同士の積は * によって計算できる。
ITensor phi = Sx * psi;
ここで注意したいのが、ここで計算するのは
\begin{equation}
\phi_{s'} = \sum_{s} S^x_{s,s'} \psi_s
\end{equation}
だということである。
積を定義したとき、同じ添字に関して和をとることになる。$\phi_{s'}$を$\phi_s$とするためには
phi.noprime();
とすればよい。
===== 期待値 =====
最後に、演算子の期待値の求め方を紹介する。
状態$\psi_s$とエルミート共役$\psi^*_{s'}$は次のように作る。
auto cpsi = dag(prime(psi));
スピン演算子の期待値は積 cpsi * Sz * psi あるいは cpsi * Sx * psi により得られるが、このままだとオブジェクトのままなので、実数として扱うためにメソッド.realを使う。
Real zz = (cpsi * Sz * psi).real();
Real xx = (cpsi * Sx * psi).real();
ここで計算したのは
\begin{equation}
\sum_{s,s'} \psi^*_{s'} S^x_{s, s'} \psi_{s}
\end{equation}
である(添字の順番に注意)。