{ "cells": [ { "cell_type": "markdown", "id": "92ce3600-4fff-4b9b-920b-06faac8a549f", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "# MPS Evolution with TEBD\n", "\n", "This example demonstrates use of the {class}`~quimb.tensor.tensor_tebd.TEBD` object to simulate the time evolution of matrix product states." ] }, { "cell_type": "code", "execution_count": 1, "id": "e80a9958-6b29-439d-b33e-2505a50b16e1", "metadata": {}, "outputs": [], "source": [ "%config InlineBackend.figure_formats = ['svg']\n", "import quimb as qu\n", "import quimb.tensor as qtn\n", "import numpy as np" ] }, { "cell_type": "markdown", "id": "809a3e1e-cc4f-493c-8165-5e70ad5f4a7c", "metadata": {}, "source": [ "To address an interesting and practical case (entanglement doesn't grow too much) we'll use as\n", "an initial state the all zero state apart from two flipped spins:" ] }, { "cell_type": "code", "execution_count": 2, "id": "a9bf9c98-2795-4cb4-8dee-deec2c281599", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "psi0: |00000000000000100000000000000100000000000000>\n" ] } ], "source": [ "L = 44\n", "zeros = '0' * ((L - 2) // 3)\n", "binary = zeros + '1' + zeros + '1' + zeros\n", "print('psi0:', f\"|{binary}>\")" ] }, { "cell_type": "markdown", "id": "2d531911-182a-4c70-81a9-e4a7bda7c247", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "We can turn this into a {class}`~quimb.tensor.tensor_1d.MatrixProductState` using\n", "{func}`~quimb.tensor.tensor_builder.MPS_computational_state`:" ] }, { "cell_type": "code", "execution_count": 3, "id": "4d0e35bd-4d89-4db7-a5a1-b702906cda62", "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 \n", "... >─>─>─>─>─>─>─>─>─>─>─>─>─>─>─>─>─>─>─>─>─>─>─>─>─>─>─>─>─>─>─>─>─>─>─ ...\n", " │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ \n", " ... \n", " 1 1 1 1 1 1 1 1 \n", " >─>─>─>─>─>─>─>─●\n", " │ │ │ │ │ │ │ │ │\n" ] } ], "source": [ "psi0 = qtn.MPS_computational_state(binary)\n", "psi0.show() # prints ascii representation of state" ] }, { "cell_type": "markdown", "id": "adb5017a-0c54-4fa4-9469-edad4d164186", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "To set the hamiltonian, we need to define a nearest neighbour interaction\n", "({class}`~quimb.tensor.tensor_tebd.LocalHam1D`) hamiltonian. These can built directly\n", "using arrays representing the one and two site terms, or from\n", "a {class}`~quimb.tensor.tensor_builder.SpinHam1D`.\n", "But, for the Heisenberg hamiltonian with no magnetic field,\n", "\n", "$$\n", "\\hat{H} = J \\sum_{i=1}^{L - 1} \\mathbf{\\sigma}_i \\cdot \\mathbf{\\sigma}_{i + 1} ~ ,\n", "$$\n", "\n", "we'll use a built-in:" ] }, { "cell_type": "code", "execution_count": 4, "id": "0b884157-d06e-4b37-a242-b0ca997ba78a", "metadata": {}, "outputs": [], "source": [ "H = qtn.ham_1d_heis(L)" ] }, { "cell_type": "markdown", "id": "3dd5b47c-d9b0-4288-ac2a-c6677c0182f4", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "Now we are ready to set-up the actual {class}`~quimb.tensor.tensor_tebd.TEBD` object,\n", "which trys to mimic the api of the normal {class}`~quimb.evo.Evolution` object.\n", "\n", "We could set a default timestep `dt` here, or instead, a default tolerance `tol`,\n", "which calculates `dt` automatically based on the total evolution length, trotter\n", "decomposition order, and hamiltonian norm." ] }, { "cell_type": "code", "execution_count": 5, "id": "9cd70a0c-9ec2-42ba-9cb8-ae1ba2fcf30b", "metadata": {}, "outputs": [], "source": [ "tebd = qtn.TEBD(psi0, H)\n", "\n", "# Since entanglement will not grow too much, we can set quite\n", "# a small cutoff for splitting after each gate application\n", "tebd.split_opts['cutoff'] = 1e-12" ] }, { "cell_type": "markdown", "id": "7ace1f11-d2ed-475f-bb97-e266cb2a7679", "metadata": {}, "source": [ "We'll also set up some parameters and quantities to compute during the evolution:" ] }, { "cell_type": "code", "execution_count": 6, "id": "d632fa8d-e0d3-45a1-a9d1-f7cda13273da", "metadata": {}, "outputs": [], "source": [ "# times we are interested in\n", "ts = np.linspace(0, 80, 101)\n", "\n", "mz_t_j = [] # z-magnetization\n", "be_t_b = [] # block entropy\n", "sg_t_b = [] # schmidt gap\n", "\n", "# range of bonds, and sites\n", "js = np.arange(0, L)\n", "bs = np.arange(1, L)" ] }, { "cell_type": "markdown", "id": "225dcbbc-7822-4a1b-9f86-5f8b3afc5eef", "metadata": {}, "source": [ "Now we are ready to being the evolution, we use the generator\n", "``at_times`` to yield the state at each target time, and set\n", "a ``tol`` here which will calculate a timestep to use.\n", "At each time, we'll compute the desired quantities and add them\n", "to our results." ] }, { "cell_type": "code", "execution_count": 7, "id": "d7849964-e3d2-4a43-820d-7a9e9b10878f", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "t=80, max-bond=15: 100%|##########| 101/101 [02:05<00:00, 1.25s/it] \n" ] } ], "source": [ "# generate the state at each time in ts\n", "# and target error 1e-3 for whole evolution\n", "for psit in tebd.at_times(ts, tol=1e-3):\n", " mz_j = []\n", " be_b = []\n", " sg_b = []\n", "\n", " # there is one more site than bond, so start with mag\n", " # this also sets the orthog center to 0, which ``info`` then tracks\n", " info = {\"cur_orthog\": None}\n", " mz_j += [psit.magnetization(0, info=info)]\n", "\n", " for j in range(1, L):\n", " mz_j += [psit.magnetization(j, info=info)]\n", " be_b += [psit.entropy(j, info=info)]\n", " sg_b += [psit.schmidt_gap(j, info=info)]\n", "\n", " mz_t_j += [mz_j]\n", " be_t_b += [be_b]\n", " sg_t_b += [sg_b]" ] }, { "cell_type": "code", "execution_count": 8, "id": "c05b624f-9b64-4660-b39c-0b22a50022fe", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 2 4 5 6 7 8 9 10 11 12 13 14 14 14 15 14 14 15 14 14 14 15 15 15 14 1 \n", "... >─>─>─>─>─>─>─>──>──>──>──>──>──>──>──>──>──>──>──>──>──>──>──>──>──>─ ...\n", " │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ \n", " ... \n", " 5 14 14 14 15 14 14 13 12 11 10 9 8 7 6 5 4 2 \n", " ─>──>──>──>──>──>──>──>──>──>──>─>─>─>─>─>─>─●\n", " │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │\n" ] } ], "source": [ "tebd.pt.show()" ] }, { "cell_type": "markdown", "id": "3364030c-00f1-416c-98ad-40cc06543500", "metadata": {}, "source": [ "``TEBD.err`` contains a (rough) upper estimate for the error\n", "incurred by the trotter decomposition so far:" ] }, { "cell_type": "code", "execution_count": 9, "id": "5523610d-4332-4eca-aef9-2394c736094c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.0009938931328611381" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tebd.err # should be < tol=1e-3" ] }, { "cell_type": "markdown", "id": "a170886c-e419-42f6-af5d-ec219d3bb488", "metadata": {}, "source": [ "We can also check the normalization of final state:" ] }, { "cell_type": "code", "execution_count": 10, "id": "ad72dc27-8099-4029-b9d8-c2201af20774", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.9999999878089972" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tebd.pt.H @ tebd.pt" ] }, { "cell_type": "markdown", "id": "55a48856-d8aa-46c9-a681-853d00d6ba65", "metadata": {}, "source": [ "And that energy has been conserved:" ] }, { "cell_type": "code", "execution_count": 11, "id": "325e76c6-b14c-48f3-b1b3-4d42b7edad1c", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Initial energy: 8.75\n", "Final energy: 8.749999702600311\n" ] } ], "source": [ "H = qtn.MPO_ham_heis(L)\n", "print(\"Initial energy:\", qtn.expec_TN_1D(psi0.H, H, psi0))\n", "print(\"Final energy:\", qtn.expec_TN_1D(tebd.pt.H , H, tebd.pt))" ] }, { "cell_type": "markdown", "id": "330fa6f4-db9b-4310-a556-20e212d740a9", "metadata": {}, "source": [ "Finally let's plot the quantities we calculated:" ] }, { "cell_type": "code", "execution_count": 12, "id": "5186ef0f-819d-485e-a718-ca8808db8bb0", "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt" ] }, { "cell_type": "code", "execution_count": 13, "id": "bca9f278-086c-4e7f-9085-a6f08388fcb3", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "with plt.style.context(qu.NEUTRAL_STYLE):\n", " plt.figure(figsize=(12, 7))\n", "\n", " # plot the magnetization\n", " ax1 = plt.subplot(131)\n", " plt.pcolormesh(js, ts, np.real(mz_t_j), vmin=-0.5, vmax=0.5)\n", " plt.set_cmap('RdYlBu')\n", " plt.colorbar()\n", " plt.title('Z-Magnetization')\n", " plt.xlabel('Site')\n", " plt.ylabel('time [ $Jt$ ]')\n", "\n", " # plot the entropy\n", " ax2 = plt.subplot(132, sharey=ax1)\n", " plt.pcolormesh(bs, ts, be_t_b)\n", " plt.setp(ax2.get_yticklabels(), visible=False)\n", " plt.set_cmap('viridis'), plt.colorbar()\n", " plt.title('Block Entropy')\n", " plt.xlabel('Bond')\n", "\n", " # plot the schmidt gap\n", " ax3 = plt.subplot(133, sharey=ax1)\n", " plt.pcolormesh(bs, ts, sg_t_b, vmin=0, vmax=1)\n", " plt.setp(ax3.get_yticklabels(), visible=False)\n", " plt.set_cmap('magma_r')\n", " plt.colorbar()\n", " plt.title('Schmidt Gap')\n", " plt.xlabel('Bond')\n", "\n", " plt.show()" ] }, { "cell_type": "markdown", "id": "ba0cc32d-82d2-4446-8885-955d898824d0", "metadata": {}, "source": [ "Where we can see ballistic propagation (and reflection) of the 'light cone'." ] }, { "cell_type": "markdown", "id": "e37ef86e-b315-4624-a88e-a147f66e2754", "metadata": {}, "source": [ "## Non-translationally invariant Hamiltonians\n", "\n", "The ``LocalHam1D`` class can also handle Hamiltonians with site specific interactions and fields.\n", "A good example of this is the Many-Body-Localized spin hamiltonian:\n", "\n", "$$\n", " \\hat{H} = J \\sum_{i=1}^{L - 1} \\mathbf{\\sigma}_i \\cdot \\mathbf{\\sigma}_{i + 1}\n", " + \\sum_i^L h_i \\sigma^Z_i\n", "$$\n", "\n", "Where $h_i$ is a random variable. Here we construct it manually:" ] }, { "cell_type": "code", "execution_count": 14, "id": "e33241a4-7087-46e9-b87a-06e6f367d360", "metadata": {}, "outputs": [], "source": [ "builder = qtn.SpinHam1D(S=1/2)\n", "\n", "# specify the interaction term (defaults to all sites)\n", "builder += 0.5, '+', '-'\n", "builder += 0.5, '-', '+'\n", "builder += 1.0, 'Z', 'Z'\n", "\n", "# add random z-fields to each site\n", "np.random.seed(2)\n", "for i in range(L):\n", " builder[i] += 2 * np.random.rand() - 1, 'Z'\n", "\n", "H = builder.build_local_ham(L)" ] }, { "cell_type": "markdown", "id": "befdaa15-8954-46c5-858e-715ef381e724", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ "But there is also the built-in {func}`~quimb.tensor.tensor_builder.ham1d_mbl`\n", "which has options for various random noise types etc.\n", "\n", "We can perform the same TEBD evolution as above with this new Hamiltonian:" ] }, { "cell_type": "code", "execution_count": 15, "id": "ab8f7b81-12bd-46a6-82ae-d0aec509d857", "metadata": {}, "outputs": [], "source": [ "tebd = qtn.TEBD(psi0, H)\n", "tebd.split_opts['cutoff'] = 1e-10\n", "\n", "# times we are interested in\n", "ts = np.linspace(0, 80, 101)\n", "\n", "mz_t_j = [] # z-magnetization\n", "be_t_b = [] # block entropy\n", "sg_t_b = [] # schmidt gap\n", "\n", "# range of bonds, and sites\n", "js = np.arange(0, L)\n", "bs = np.arange(1, L)" ] }, { "cell_type": "code", "execution_count": 16, "id": "9c93c435-e231-4989-84d0-5425689f9de9", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "t=80, max-bond=19: 100%|##########| 101/101 [02:45<00:00, 1.64s/it] \n" ] } ], "source": [ "# generate the state at each time in ts\n", "# and target error 1e-3 for whole evolution\n", "for psit in tebd.at_times(ts, tol=1e-3):\n", " mz_j = []\n", " be_b = []\n", " sg_b = []\n", "\n", " # there is one more site than bond, so start with mag\n", " # this also sets the orthog center to 0, which ``info`` then tracks\n", " info = {\"cur_orthog\": None}\n", " mz_j += [psit.magnetization(0, info=info)]\n", "\n", " for j in range(1, L):\n", " mz_j += [psit.magnetization(j, info=info)]\n", " be_b += [psit.entropy(j, info=info)]\n", " sg_b += [psit.schmidt_gap(j, info=info)]\n", "\n", " mz_t_j += [mz_j]\n", " be_t_b += [be_b]\n", " sg_t_b += [sg_b]" ] }, { "cell_type": "markdown", "id": "ac0d7341-91ab-4b69-b0a5-1be4ed2e9ac9", "metadata": {}, "source": [ "And finally, plot the quantities again:" ] }, { "cell_type": "code", "execution_count": 17, "id": "3369c072-5577-458a-b752-32b629212b33", "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "with plt.style.context(qu.NEUTRAL_STYLE):\n", " plt.figure(figsize=(12, 7))\n", "\n", " # plot the magnetization\n", " ax1 = plt.subplot(131)\n", " plt.pcolormesh(js, ts, np.real(mz_t_j), vmin=-0.5, vmax=0.5)\n", " plt.set_cmap('RdYlBu')\n", " plt.colorbar()\n", " plt.title('Z-Magnetization')\n", " plt.xlabel('Site')\n", " plt.ylabel('time [ $Jt$ ]')\n", "\n", " # plot the entropy\n", " ax2 = plt.subplot(132, sharey=ax1)\n", " plt.pcolormesh(bs, ts, be_t_b)\n", " plt.setp(ax2.get_yticklabels(), visible=False)\n", " plt.set_cmap('viridis'), plt.colorbar()\n", " plt.title('Block Entropy')\n", " plt.xlabel('Bond')\n", "\n", " # plot the schmidt gap\n", " ax3 = plt.subplot(133, sharey=ax1)\n", " plt.pcolormesh(bs, ts, sg_t_b, vmin=0, vmax=1)\n", " plt.setp(ax3.get_yticklabels(), visible=False)\n", " plt.set_cmap('magma_r')\n", " plt.colorbar()\n", " plt.title('Schmidt Gap')\n", " plt.xlabel('Bond')\n", "\n", " plt.show()" ] }, { "cell_type": "markdown", "id": "908eebeb-b974-49aa-b2dc-325efa22f53b", "metadata": {}, "source": [ "Here we can see much more confinement." ] } ], "metadata": { "celltoolbar": "Raw Cell Format", "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" }, "vscode": { "interpreter": { "hash": "39c10650315d977fb13868ea1402e99f3e10e9885c2c202e692ae90b8995050d" } } }, "nbformat": 4, "nbformat_minor": 4 }