{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# A3 - 古典イジング模型" ] }, { "cell_type": "markdown", "metadata": { "colab_type": "text", "id": "view-in-github" }, "source": [ "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/OpenJij/OpenJijTutorial/blob/master/source/ja/A003-LargeScaleMC.ipynb)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "この章ではOpenJijのcore interface (core python interface)の使い方を説明し、簡単な計算のデモンストレーションを行います。\n", "\n", "core interfaceは前回までのチュートリアルよりも下部のレイヤーのAPIです。対象読者としては前回までのOpenJijチュートリアルを一通り終えて、イジングモデルやモンテカルロ法などの用語を知っている方を想定します。次のような目的を持った読者を想定しています。\n", "\n", "* 最適化問題だけでなくサンプリングや研究用途などより専門的な用途にOpenJijを用いたい\n", "* アニーリングスケジュールの設定や使用するアルゴリズム等を直接触りたい" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import cxxjij.graph as G\n", "#問題サイズを100とします。\n", "N = 100\n", "\n", "graph = G.Dense(N)\n", "#sparseの場合は以下を使用します。\n", "#graph = G.Sparse(N)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "$J_{ij}, h_i$を設定します。今回は平均0、標準偏差1のGauss分布から生成される値を設定します。" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!pip install numpy #乱数生成にnumpyを使います。" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "mu, sigma = 0, 1\n", "\n", "for i in range(N):\n", " for j in range(N):\n", " #Jijの値が大きくなりすぎてしまうので、1/Nで規格化を行なっています。\n", " graph[i,j] = 0 if i == j else np.random.normal()/N\n", "\n", "for i in range(N):\n", " graph[i] = np.random.normal()/N" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "縦磁場に関しては、`graph[i]`でも、`graph[i,i]`でもどちらでもアクセスできます。また、イジングモデルの定義上、$J_{ij}$と$J_{ji}$は自動で同じ値となります。試しに以下のように出力を行なってみましょう。" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0.5\n", "0.5\n", "-0.6\n", "-0.6\n" ] } ], "source": [ "graph[20] = 0.5\n", "print(graph[20,20])\n", "print(graph[20])\n", "graph[12,34] = -0.6\n", "print(graph[12,34])\n", "print(graph[34,12])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## システムの設定 - System -\n", "\n", "続いて計算を行うためのシステムを定義します。ここでは古典イジング模型か横磁場イジング模型か、また別の模型にするか等を選べます。 \n", "\n", "まずは古典イジング模型のシステムを作成してみます。`system.make_classical_ising`で作成できます。" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "import cxxjij.system as S\n", "\n", "mysystem = S.make_classical_ising(graph.gen_spin(), graph)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ここで、1つ目の引数にはランダムに生成したスピン、2つめにはGraphそのものを代入します。これにより初期のスピン配位が`graph.gen_spin()`となる古典イジング模型のシステムの作成ができます。\n", "\n", "システムに直接アクセスして、値を読むことも可能です。" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[-1. 1. -1. -1. 1. -1. -1. 1. -1. 1. 1. 1. -1. -1. 1. -1. 1. -1.\n", " -1. 1. -1. 1. -1. -1. 1. 1. 1. -1. 1. -1. -1. -1. 1. -1. 1. -1.\n", " 1. -1. 1. -1. 1. -1. 1. 1. -1. -1. -1. 1. -1. 1. 1. -1. 1. 1.\n", " -1. -1. -1. 1. 1. 1. -1. 1. 1. 1. -1. -1. -1. -1. -1. 1. 1. -1.\n", " -1. 1. 1. 1. 1. -1. 1. 1. 1. 1. 1. -1. -1. 1. -1. -1. -1. -1.\n", " 1. 1. -1. 1. -1. -1. -1. -1. -1. -1. 1.]\n" ] } ], "source": [ "print(mysystem.spin)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "古典イジング模型以外にも様々なシステムが用意されており、これを用途別に使うことが出来ます。用いるSystemによって初期化の方法は多少異なります。これは後ほど少しずつ紹介していきます。\n", "\n", "## アルゴリズムの実行 -Updater, Algorithm-\n", "\n", "Systemを定義した後はUpdaterを選択し、Algorithmを実行していきます。\n", "\n", "### Updater\n", "\n", "Systemに対して使用できるUpdaterは決められており、古典イジング模型に対するUpdaterは主に\n", "\n", "- [SingleSpinFlip](https://github.com/OpenJij/OpenJij/blob/ec41aecfbac7e4c895e1e7a1718f06eb7ffae0ba/src/updater/single_spin_flip.hpp#L40) (メトロポリス・ヘイスティング法によるスピン1つずつのアップデート)\n", "- [SwendsenWang](https://github.com/OpenJij/OpenJij/blob/ec41aecfbac7e4c895e1e7a1718f06eb7ffae0ba/src/updater/swendsen_wang.hpp#L45) (SwendsenWang法によるクラスターアップデート)\n", "\n", "の2つが用意されています。\n", "Algorithmを実行するには、**スケジュールリスト**が必要になります。まずスケジュールリストを作成するところから始めましょう。\n", "\n", "### Algorithm\n", "\n", "#### スケジュールリスト\n", "\n", "スケジュールリストは`(パラメータ, モンテカルロステップ数)`のリストで与えられるものです。パラメータ部分に入力する値はSystemによって異なります。例えば、古典イジング模型ならばパラメータとして温度の逆数である逆温度$\\beta$を設定します。例として以下のように設定してみましょう。" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "schedule_list = [(0.01, 10),(10, 80),(0.1, 30)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "この場合、逆温度$\\beta=0.01$で10モンテカルロステップ、$\\beta=10$で80ステップ、$\\beta=0.1$で30ステップの計120モンテカルロステップを実行することを意味します。 \n", "アニーリングを実行するにあたっては、逆温度は等比級数で増加させていくことが多いため、以下のように`utility`にある`make_classical_schedule_list`を使うとより便利です。" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[((beta: 0.100000) mcs: 20), ((beta: 0.199474) mcs: 20), ((beta: 0.397897) mcs: 20), ((beta: 0.793701) mcs: 20), ((beta: 1.583223) mcs: 20), ((beta: 3.158114) mcs: 20), ((beta: 6.299605) mcs: 20), ((beta: 12.566053) mcs: 20), ((beta: 25.065966) mcs: 20), ((beta: 50.000000) mcs: 20)]\n" ] } ], "source": [ "import cxxjij.utility as U\n", "schedule_list = U.make_classical_schedule_list(0.1, 50, 20, 10)\n", "print(schedule_list)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "上の例では$\\beta=0.1$から$\\beta=50$まで、各温度で20モンテカルロステップ計算しながら10段階で温度を変えていく設定例です。計200モンテカルロステップの計算を行います。\n", "\n", "#### Algorithmの実行\n", "\n", "続いて、Algorithmを実行します。`Algorithm_[Updater]_run`のように書くことで、指定したUpdaterで計算を行います。次の例ではSingleSpinFlipを実行しています。" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "import cxxjij.algorithm as A\n", "A.Algorithm_SingleSpinFlip_run(mysystem, schedule_list)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "一瞬で処理が終わりましたが、この間に計200モンテカルロステップの計算が行われています。\n", "> `A.Algorithm_SingleSpinFlip_run(mysystem, seed, schedule_list)`とすることで、seedを固定したまま計算を行うことができます。結果に再現性をもたせたい際に使うことができます。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "callbackを使用することで、Algorithmの実行中に1モンテカルロステップごとのシステムを取得することができます。古典イジング模型の場合は、システムとパラメータ (逆温度)を引数を持つ関数を作成すれば良いです。 \n", "例として、以下ではシステムのエネルギーの値を記録するcallbackを作っています。" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "energies = []\n", "\n", "def callback_log_energy(system, beta):\n", " #graphは以前にGraphモジュールにて定義したオブジェクトです\n", " energies.append(graph.calc_energy(system.spin))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "このcallbackを用いて同じAlgorithmを実行します。" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "#スケジュールをもっと長く取ります (計20000モンテカルロステップ)\n", "schedule_list = U.make_classical_schedule_list(0.1, 50, 200, 100)\n", "A.Algorithm_SingleSpinFlip_run(mysystem, schedule_list, callback_log_energy)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "記録したシステムのエネルギーを、横軸をモンテカルロステップ、縦軸をエネルギーでプロットすると次のようになります。" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!pip install matplotlib" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "plt.plot(range(len(energies)), energies)\n", "plt.xlabel('Monte Carlo step')\n", "plt.ylabel('energy')\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "アニーリングが進むに連れ徐々にエネルギーが低くなっているのが分かります。Algorithmの動作中にシステムの様子を知りたい時に有用です。\n", "\n", "## 結果の取得 -Result-\n", "\n", "`result.get_solution`で計算結果であるスピン列を取得できます。古典イジング模型の場合は直接`mysystem.spin`を参照することで、スピン列を取得も可能です。しかし、`result.get_solution`はそれ以外のシステムについてもスピン列を取得できる便利なメソッドです。" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[-1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, -1]\n" ] } ], "source": [ "import cxxjij.result as R\n", "print(R.get_solution(mysystem))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "このスピン列がアニーリングによって得られた答えです。ハミルトニアンの基底状態 (に近い状態)であることが期待されます。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## C++ core interface\n", "\n", "多少の違いはありますが、C++ core interfaceでも上記とほぼ同じことが可能です。以下の点に留意しましょう。\n", "\n", "- seed値を入れる引数には、乱数生成器 (C++11 random)を代入する必要があります。\n", "- Graphクラスで、$J_{ij}, h_i$へのアクセス方法が多少異なります。\n", "\n", "今までの内容をC++ core interfaceで記述すると次のようになります。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```cpp\n", "#include \n", "#include \n", "#include \n", "#include \n", "#include \n", "#include \n", "#include \n", "#include \n", "\n", "#include \n", "\n", "using namespace openjij;\n", "\n", "int main(void){\n", "\n", " //generate dense graph with size N=100\n", " constexpr std::size_t N = 100;\n", " auto dense = graph::Dense(N);\n", "\n", " //generate random engine\n", " auto rand_engine = std::mt19937(0x1234);\n", " //of course you can specify another random generator that is compatible with C++ random generator, say utility::Xorshift,\n", " //auto rand_engine = utility::Xorshift(0x1234);\n", " \n", " //Gaussian distribution\n", " auto gauss = std::normal_distribution<>{0, 1};\n", "\n", " //set interactions\n", " for(std::size_t i=0; i\n", " //std::vector> schedule_list;\n", " //utility::Schedule schedule;\n", " //schedule.updater_parameter = {0.01};\n", " //schedule.one_mc_step = 10; //number of Monte Carlo step per temperature\n", " //schedule_list.push_back(schedule);\n", " //\n", " //schedule.updater_parameter = {10};\n", " //schedule.one_mc_step = 80; //number of Monte Carlo step per temperature\n", " //schedule_list.push_back(schedule);\n", " //\n", " //schedule.updater_parameter = {0.1};\n", " //schedule.one_mc_step = 30; //number of Monte Carlo step per temperature\n", " //schedule_list.push_back(schedule); //schedule_list -> [(0.01, 10), (10, 80), (0.1, 30)]\n", "\n", "\n", " //do annealing (updater: SingleSpinFlip)\n", " algorithm::Algorithm::run(system, rand_engine, schedule_list);\n", "\n", " //show spins\n", " std::cout << \"The result spins are [\";\n", " for(auto&& elem : result::get_solution(system)){\n", " std::cout << elem << \" \";\n", " }\n", "\n", " std::cout << \"]\" << std::endl;\n", "}\n", "\n", "```" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.3" } }, "nbformat": 4, "nbformat_minor": 4 }