├── Intro-1-MPS.pdf ├── .gitignore ├── Intro-2-TEBD-DMRG.pdf ├── tenpy_toycodes ├── __init__.py ├── tfi_exact.py ├── lanczos.py ├── b_model.py ├── free_fermions_exact.py ├── c_tebd.py ├── a_mps.py ├── d_dmrg.py └── e_tdvp.py ├── setup.py ├── README.md ├── exercise_tenpy.ipynb ├── exercise_2_mps.ipynb ├── solution_2_mps.ipynb ├── exercise_3_dmrg.ipynb ├── exercise_toycodes.ipynb ├── exercise_1_basics.ipynb └── toycode_example_calls.ipynb /Intro-1-MPS.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhauschild/SchoolPadua2024/HEAD/Intro-1-MPS.pdf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.egg-info 3 | __pycache__ 4 | .ipynb_checkpoints 5 | *.pdf 6 | *.png 7 | -------------------------------------------------------------------------------- /Intro-2-TEBD-DMRG.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jhauschild/SchoolPadua2024/HEAD/Intro-2-TEBD-DMRG.pdf -------------------------------------------------------------------------------- /tenpy_toycodes/__init__.py: -------------------------------------------------------------------------------- 1 | # this file makes the directory a Python package that can be imported with `import toycodes` 2 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # this file allows to install toycodes of this repository, locally with 2 | # pip install -e . 3 | # or online with 4 | # pip install git+https://github.com/tenpy/tenpy_toycodes.git 5 | # to uninstall, simply 6 | # pip uninstall tenpy-toycodes 7 | 8 | from setuptools import setup 9 | 10 | setup(name='tenpy-toycodes', 11 | version='0.1.1', 12 | packages=['tenpy_toycodes']) 13 | -------------------------------------------------------------------------------- /tenpy_toycodes/tfi_exact.py: -------------------------------------------------------------------------------- 1 | """Provides exact ground state energies for the transverse field ising model for comparison. 2 | 3 | The Hamiltonian reads 4 | .. math :: 5 | H = - J \\sum_{i} \\sigma^z_i \\sigma^z_{i+1} - g \\sum_{i} \\sigma^x_i 6 | """ 7 | # Copyright (C) TeNPy Developers, GNU GPLv3 8 | 9 | import numpy as np 10 | import scipy.sparse as sparse 11 | from scipy.sparse.linalg import eigsh 12 | import warnings 13 | import scipy.integrate 14 | 15 | 16 | def finite_gs_energy(L, J, g, return_psi=False): 17 | """For comparison: obtain ground state energy from exact diagonalization. 18 | 19 | Exponentially expensive in L, only works for small enough `L` <~ 20. 20 | """ 21 | if L >= 20: 22 | warnings.warn("Large L: Exact diagonalization might take a long time!") 23 | # get single site operaors 24 | sx = sparse.csr_matrix(np.array([[0., 1.], [1., 0.]])) 25 | sz = sparse.csr_matrix(np.array([[1., 0.], [0., -1.]])) 26 | id = sparse.csr_matrix(np.eye(2)) 27 | sx_list = [] # sx_list[i] = kron([id, id, ..., id, sx, id, .... id]) 28 | sz_list = [] 29 | for i_site in range(L): 30 | x_ops = [id] * L 31 | z_ops = [id] * L 32 | x_ops[i_site] = sx 33 | z_ops[i_site] = sz 34 | X = x_ops[0] 35 | Z = z_ops[0] 36 | for j in range(1, L): 37 | X = sparse.kron(X, x_ops[j], 'csr') 38 | Z = sparse.kron(Z, z_ops[j], 'csr') 39 | sx_list.append(X) 40 | sz_list.append(Z) 41 | H_zz = sparse.csr_matrix((2**L, 2**L)) 42 | H_x = sparse.csr_matrix((2**L, 2**L)) 43 | for i in range(L - 1): 44 | H_zz = H_zz + sz_list[i] * sz_list[(i + 1) % L] 45 | for i in range(L): 46 | H_x = H_x + sx_list[i] 47 | H = -J * H_zz - g * H_x 48 | E, V = eigsh(H, k=1, which='SA', return_eigenvectors=True, ncv=20) 49 | if return_psi: 50 | return E[0], V[:, 0] 51 | return E[0] 52 | 53 | 54 | def infinite_gs_energy(J, g): 55 | """For comparison: Calculate groundstate energy density from analytic formula. 56 | 57 | The analytic formula stems from mapping the model to free fermions, see P. Pfeuty, The one- 58 | dimensional Ising model with a transverse field, Annals of Physics 57, p. 79 (1970). Note that 59 | we use Pauli matrices compared this reference using spin-1/2 matrices and replace the sum_k -> 60 | integral dk/2pi to obtain the result in the N -> infinity limit. 61 | """ 62 | def f(k, lambda_): 63 | return np.sqrt(1 + lambda_**2 + 2 * lambda_ * np.cos(k)) 64 | 65 | E0_exact = -g / (J * 2. * np.pi) * scipy.integrate.quad(f, -np.pi, np.pi, args=(J / g, ))[0] 66 | return E0_exact 67 | -------------------------------------------------------------------------------- /tenpy_toycodes/lanczos.py: -------------------------------------------------------------------------------- 1 | """Simple Lanczos diagonalization routines. 2 | 3 | These functions can be used in d_dmrg.py and e_tdvp to replace 4 | the eigsh and expm_mulitply calls, as commented out in the code. 5 | """ 6 | # Copyright (C) TeNPy Developers, GNU GPLv3 7 | 8 | from scipy.sparse.linalg import expm 9 | import numpy as np 10 | import warnings 11 | 12 | default_k = 20 13 | 14 | def lanczos_ground_state(H, psi0, k=None): 15 | """Use lanczos to calculated the ground state of a hermitian H. 16 | 17 | If you don't know Lanczos, you can view this as a black box algorithm. 18 | The idea is to built an orthogonal basis in the Krylov space spanned by 19 | ``{ H^i |psi0> for i < k}`` and find the ground state in this sub space. 20 | It only requires an `H` which defines a matrix-vector multiplication. 21 | """ 22 | T, vecs = lanczos_iterations(H, psi0, k) 23 | E, v = np.linalg.eigh(T) 24 | result = vecs @ v[:, 0] 25 | if abs(np.linalg.norm(result) - 1.) > 1.e-5: 26 | warnings.warn("poorly conditioned lanczos. Maybe a non-hermitian H?") 27 | return E[0], result 28 | 29 | def lanczos_expm_multiply(H, psi0, dt, k=None): 30 | """Use lanczos to calculated ``expm(-i H dt)|psi0>`` for sufficiently small dt and hermitian H. 31 | 32 | If you don't know Lanczos, you can view this as a black box algorithm. 33 | The idea is to built an orthogonal basis in the Krylov space spanned by 34 | ``{ H^i |psi0> for i < k}`` and evolve only in this subspace. 35 | It only requires an `H` which defines a matrix-vector multiplication. 36 | """ 37 | T, vecs = lanczos_iterations(H, psi0, k) 38 | v0 = np.zeros(T.shape[0]) 39 | v0[0] = 1. 40 | vt = expm(-1.j*dt*T) @ v0 41 | result = vecs @ vt 42 | if abs(np.linalg.norm(result) - 1.) > 1.e-5: 43 | warnings.warn("poorly conditioned lanczos. Maybe a non-hermitian H?") 44 | return result 45 | 46 | def lanczos_iterations(H, psi0, k): 47 | """Perform `k` Lanczos iterations building tridiagonal matrix T and ONB of the Krylov space.""" 48 | if k is None: 49 | k = default_k 50 | if psi0.ndim != 1: 51 | raise ValueError("psi0 should be a vector") 52 | if H.shape[1] != psi0.shape[0]: 53 | raise ValueError("Shape of H doesn't match len of psi0.") 54 | psi0 = psi0/np.linalg.norm(psi0) 55 | vecs = [psi0] 56 | T = np.zeros((k, k)) 57 | psi = H @ psi0 58 | alpha = T[0, 0] = np.inner(psi0.conj(), psi).real 59 | psi = psi - alpha* vecs[-1] 60 | for i in range(1, k): 61 | beta = np.linalg.norm(psi) 62 | if beta < 1.e-13: 63 | # print(f"Lanczos terminated early after i={i:d} steps:" 64 | # "full Krylov space built") 65 | T = T[:i, :i] 66 | break 67 | psi /= beta 68 | vecs.append(psi) 69 | psi = H @ psi - beta * vecs[-2] 70 | alpha = np.inner(vecs[-1].conj(), psi).real 71 | psi = psi - alpha * vecs[-1] 72 | T[i, i] = alpha 73 | T[i-1, i] = T[i, i-1] = beta 74 | return T, np.array(vecs).T 75 | -------------------------------------------------------------------------------- /tenpy_toycodes/b_model.py: -------------------------------------------------------------------------------- 1 | """Toy code implementing the transverse-field ising model.""" 2 | # Copyright (C) TeNPy Developers, GNU GPLv3 3 | 4 | import numpy as np 5 | 6 | 7 | class TFIModel: 8 | r"""Simple class generating the Hamiltonian of the transverse-field Ising model. 9 | 10 | The Hamiltonian reads 11 | .. math :: 12 | H = - J \sum_{i} \sigma^z_i \sigma^z_{i+1} - g \sum_{i} \sigma^x_i 13 | 14 | Parameters 15 | ---------- 16 | L : int 17 | Number of sites. 18 | J, g : float 19 | Coupling parameters of the above defined Hamiltonian. 20 | bc : 'infinite', 'finite' 21 | Boundary conditions. 22 | 23 | Attributes 24 | ---------- 25 | L : int 26 | Number of sites. 27 | bc : 'infinite', 'finite' 28 | Boundary conditions. 29 | sigmax, sigmay, sigmaz, id : 30 | Local operators, namely the Pauli matrices and identity. 31 | H_bonds : list of np.Array[ndim=4] 32 | The Hamiltonian written in terms of local 2-site operators, ``H = sum_i H_bonds[i]``. 33 | Each ``H_bonds[i]`` has (physical) legs (i out, (i+1) out, i in, (i+1) in), 34 | in short ``i j i* j*``. 35 | H_mpo : lit of np.Array[ndim=4] 36 | The Hamiltonian written as an MPO. 37 | Each ``H_mpo[i]`` has legs (virtual left, virtual right, physical out, physical in), 38 | in short ``wL wR i i*``. 39 | """ 40 | def __init__(self, L, J, g, bc='finite'): 41 | assert bc in ['finite', 'infinite'] 42 | self.L, self.d, self.bc = L, 2, bc 43 | self.J, self.g = J, g 44 | self.sigmax = np.array([[0., 1.], [1., 0.]]) 45 | self.sigmay = np.array([[0., -1j], [1j, 0.]]) 46 | self.sigmaz = np.array([[1., 0.], [0., -1.]]) 47 | self.id = np.eye(2) 48 | self.init_H_bonds() 49 | self.init_H_mpo() 50 | 51 | def init_H_bonds(self): 52 | """Initialize `H_bonds` hamiltonian. 53 | 54 | Called by __init__(). 55 | """ 56 | X, Z, Id = self.sigmax, self.sigmaz, self.id 57 | d = self.d 58 | nbonds = self.L - 1 if self.bc == 'finite' else self.L 59 | H_list = [] 60 | for i in range(nbonds): 61 | gL = gR = 0.5 * self.g 62 | if self.bc == 'finite': 63 | if i == 0: 64 | gL = self.g 65 | if i + 1 == self.L - 1: 66 | gR = self.g 67 | H_bond = -self.J * np.kron(Z, Z) - gL * np.kron(X, Id) - gR * np.kron(Id, X) 68 | # note: kron is short-hand for outer product + grouping bra and ket legs. 69 | # H_bond has legs ``i, j, i*, j*`` 70 | H_list.append(np.reshape(H_bond, [d, d, d, d])) 71 | self.H_bonds = H_list 72 | 73 | # (note: not required for TEBD) 74 | def init_H_mpo(self): 75 | """Initialize `H_mpo` Hamiltonian. 76 | 77 | Called by __init__(). 78 | """ 79 | w_list = [] 80 | for i in range(self.L): 81 | w = np.zeros((3, 3, self.d, self.d), dtype=float) 82 | w[0, 0] = w[2, 2] = self.id 83 | w[0, 1] = self.sigmaz 84 | w[0, 2] = -self.g * self.sigmax 85 | w[1, 2] = -self.J * self.sigmaz 86 | w_list.append(w) 87 | self.H_mpo = w_list 88 | -------------------------------------------------------------------------------- /tenpy_toycodes/free_fermions_exact.py: -------------------------------------------------------------------------------- 1 | r"""Provides exact entropies for time evolution of free fermion models.""" 2 | # Copyright (C) TeNPy Developers, GNU GPLv3 3 | 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | 7 | 8 | def hopping_matrix(L, t=1., mu_staggered=0., boundary_conditions='open'): 9 | r"""Generate matrix for fermionic hopping in real-space c_i operators. 10 | 11 | Provide hopping matrix h_{ij} for 12 | 13 | .. math :: 14 | H = \sum_{ij} h_{ij} c_i^\dagger c_j 15 | = t \sum_{i} (c_i^\dagger c^\j + h.c.) + \mu_s (-1)^{i} n_i 16 | 17 | for open boundary conditions on L sites. 18 | """ 19 | assert boundary_conditions in ['open', 'periodic'] 20 | H = np.zeros((L,L)) 21 | for i in range(L - int(boundary_conditions == 'open')): 22 | H[i, (i+1) % L] = t 23 | H[(i+1) % L, i] = t 24 | for i in range(L): 25 | H[i, i] = mu_staggered*(-1)**i 26 | return H 27 | 28 | 29 | def charge_density_wave(L): 30 | """Initial state |010101010...>""" 31 | psi0 = np.mod(np.arange(0,L),2) 32 | return np.diag(psi0) 33 | 34 | 35 | def time_evolved_state(H_hop, psi0, time_list): 36 | """Evolve an initial correlation matrix with H_hop.""" 37 | E, U = np.linalg.eigh(H_hop) 38 | for time in time_list: 39 | X = np.dot(np.dot(np.conj(U),np.diag(np.exp(1j*time*E))),U.T) 40 | psi_t = np.dot(X,np.dot(psi0, np.conj(X.T))) 41 | yield psi_t # c^dagger_i c_j 42 | 43 | 44 | def entanglement_entropy(psi, bond=None): 45 | """Calculate entanglement entropy for cutting at given bond (default: half-chain).""" 46 | L = psi.shape[0] 47 | if bond is None: 48 | bond = L // 2 49 | z = np.linalg.eigvalsh(psi[:bond, :bond]) 50 | z = z[np.logical_and(z > 1.e-15, z < 1. - 1.e-15)] 51 | S = - np.sum(z * np.log(z) + (1. - z) * np.log(1. - z)) 52 | return S 53 | 54 | 55 | def XX_model_ground_state_energy(L, h_staggered, boundary_conditions='open'): 56 | """ 57 | """ 58 | H_hop = hopping_matrix(L, 2., 2.*h_staggered, boundary_conditions=boundary_conditions) 59 | E = np.linalg.eigvalsh(H_hop) 60 | E_shift = 0. if L % 2 == 0 else - h_staggered 61 | # the shift stems from the 1/2 in mapping sigmaz = (n - 1/2) for the h_s terms 62 | # which cancels out for even L due to the alternating sign of h_s 63 | return np.sum(E[:L//2]) + E_shift 64 | 65 | def XX_model_time_evolved_entropies(L, h_staggered, time_list, bond=None, 66 | boundary_conditions='open'): 67 | r"""Half-chain entanglement entropies for time evolving Neel state with XX chain. 68 | 69 | The XX chain given by the hamiltonian (here, X,Y,Z = Pauli matrices) 70 | 71 | .. math :: 72 | H = \sum_{i} (X_i X_{i+1} + Y_i Y_{i+1}) - h_s (-1)^i Z_i 73 | 74 | maps to free fermions through the Jordan-Wigner transformation. 75 | This function returns the entropies for a time evolution starting from the Neel state 76 | |up down up down ...>. 77 | """ 78 | psi_0 = charge_density_wave(L) 79 | H_hop = hopping_matrix(L, 2., h_staggered, boundary_conditions=boundary_conditions) 80 | S_list = [] 81 | for psi_t in time_evolved_state(H_hop, psi_0, time_list): 82 | S_list.append(entanglement_entropy(psi_t)) 83 | return np.array(S_list) 84 | 85 | 86 | if __name__ == "__main__": 87 | L = 100 88 | h_staggered = 1. 89 | time_list = np.linspace(0, 20., 100) 90 | for h_s in [0., 1., 2.]: 91 | S_list = XX_model_time_evolved_entropies(L, h_s, time_list) 92 | plt.plot(time_list, S_list, label="$h_s = {h_s:1.1f}$".format(h_s=h_s)) 93 | h_s = 0. 94 | S_list = XX_model_time_evolved_entropies(L, h_s, time_list, 95 | boundary_conditions='periodic') 96 | plt.plot(time_list, S_list, linestyle='--', 97 | label="$h_s = {h_s:1.1f}$, periodic".format(h_s=h_s)) 98 | S_list = XX_model_time_evolved_entropies(10, h_s, time_list) 99 | plt.plot(time_list, S_list, linestyle=':', 100 | label="$h_s = {h_s:1.1f}, L=10$".format(h_s=h_s)) 101 | 102 | plt.xlabel('$t$') 103 | plt.ylabel('$S$') 104 | plt.legend(loc='best') 105 | plt.show() 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tutorials on Tensor Networks and Matrix product states 2 | 3 | This is a set of tutorials used in various (Winter-/Summer-)Schools on Tensor Networks, e.g. from the [European Tensor Network](http://quantumtensor.eu/). Each school has it's on branch with the final notebooks used. 4 | 5 | This "main" branch keeps a general set of notebooks that can be used as an introduction and that forms the basis for the tutorials in the various schools, and is also included into the main TeNPy documentation. 6 | 7 | The tutorials are split into two parts as follows. 8 | 9 | In the first part, the `exercise_1_*.ipynb` notebooks use a set of small "toy codes", small python scripts that require only [Python](https://python.org) with [numpy](https://numpy.org) + [scipy](https://scipy.org) + [matplotlib](https://matplotlib.org). They should give you a good idea how the algorithms work without the need to understand a full library like TeNPy. 10 | These python files for this are in the folder `tenpy_toycodes`, and you need to look into them during the tutorials to see how they work. It should not be necessary to modify the python files, however; you can define all functions etc in the notebooks itself. This will ensure that the other notebooks using them keep working. 11 | The exercises itself are in interactive [jupyter notebooks](https://jupyter.org), where you can have code, output and plots together. 12 | 13 | The `exercise_tenpy.ipynb` notebook is for the second part, and uses the [TeNPy](https://github.com/tenpy/tenpy) library to demonstrate first examples of calling TeNPy rather than the toycodes. 14 | 15 | **DISCLAIMER**: The toycodes and examples used are not optimized, and we only use very small bond dimensions here to make sure everything runs quickly on a normal laptop. For state-of-the-art MPS calculations (especially for cylinders towards 2D), `chi` should be significantly larger, often on the order of several 1000s (and significantly more CPU time). 16 | 17 | ## Some References 18 | 19 | - [White, PRL 69, 2863 (1992)](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.69.2863), the original DMRG paper! 20 | - [Hauschild, Pollmann, arXiv:1805.0055](https://arxiv.org/abs/1805.0055), review with focus on TeNPy use cases 21 | - [Schollwoeck, arXiv:1008.3477](https://arxiv.org/abs/1008.3477), a classic review 22 | - [Haegeman et al, arXiv:1103.0936](https://arxiv.org/abs/1103.0936), the original application of TDVP to MPS 23 | - [Haegeman et al, arXiv:1408.5056](https://arxiv.org/abs/1408.5056), discussed in the TDVP lecture 24 | - [Vanderstraeten et al, arXiv:1810.07006](https://arxiv.org/abs/1810.07006), a good review of the tangent space for infinite MPS 25 | - [Paeckel et al, arXiv:1901.05824](https://arxiv.org/abs/1901.05824), a nice review comparing various MPS time evolution methods 26 | - [More references in the TeNPy docs](https://tenpy.readthedocs.io/en/latest/literature.html) 27 | 28 | 29 | ## Setup 30 | 31 | **Running locally**: If you have a working Python installation, feel free to solve all the exercises locally on your own computer. For the most part, you only need the `*.ipynb` notebooks and the `tenpy_toycodes/` folder. 32 | For the second part, you need to [install TeNPy](https://tenpy.readthedocs.io/en/latest/INSTALL.html), which is often just a `conda install physics-tenpy` or `pip install physics-tenpy`, depending on your setup. 33 | 34 | **Jupyter notebooks**: We recommend solving the exercises interactively with [jupyter notebooks](https//jupyter.org). You can get it with `conda install jupyterlab` or `pip install jupyterlab` and then run `jupyter-lab`, which opens an interactive coding session in your web browser. 35 | 36 | **Running notebooks on Google colab**: You can also use [Google's colab cloud service](https://colab.research.google.com) to run the jupyter notebooks **without any local installation**. Use this option if you have any trouble with your local installation. However, you have to perform addiontal installs: 37 | - For the first part, `exercise_1_*.ipynb`, you need to make sure that you not only copy the notebooks itself onto google colab, but also the `tenpy_toycodes/` folder (including the `__init__.py` file). 38 | Alternatively, install them by adding and executing a notebook cell `!pip install git+https://github.com/tenpy/tenpy_toycodes.git` at the top of the notebooks. 39 | - For the second part, `exercise_tenpy.ipynb`, you need to install TeNPy. On google colab, this can be done by adding and executing a notebook cell `!pip install physics-tenpy` at the top of the notebook. 40 | 41 | 42 | ## License 43 | 44 | All codes are released under GPL (v3) given in the file `LICENSE`, which means you can freely copy, share and distribute the code. 45 | They toycodes in the folder `tenpy_toycodes` have formerly been distributed directly from the [TeNPy](https://github.com/tenpy/tenpy) repository (also under the GPL). 46 | -------------------------------------------------------------------------------- /tenpy_toycodes/c_tebd.py: -------------------------------------------------------------------------------- 1 | """Toy code implementing the time evolving block decimation (TEBD).""" 2 | # Copyright (C) TeNPy Developers, GNU GPLv3 3 | 4 | import numpy as np 5 | from scipy.linalg import expm 6 | from .a_mps import split_truncate_theta 7 | 8 | 9 | def calc_U_bonds(H_bonds, dt): 10 | """Given the H_bonds, calculate ``U_bonds[i] = expm(-dt*H_bonds[i])``. 11 | 12 | Each local operator has legs (i out, (i+1) out, i in, (i+1) in), in short ``i j i* j*``. 13 | Note that no imaginary 'i' is included, thus real `dt` means 'imaginary time' evolution! 14 | """ 15 | d = H_bonds[0].shape[0] 16 | U_bonds = [] 17 | for H in H_bonds: 18 | H = np.reshape(H, [d * d, d * d]) 19 | U = expm(-dt * H) 20 | U_bonds.append(np.reshape(U, [d, d, d, d])) 21 | return U_bonds 22 | 23 | 24 | def run_TEBD(psi, U_bonds, N_steps, chi_max, eps): 25 | """Evolve for `N_steps` time steps with TEBD.""" 26 | Nbonds = psi.L - 1 if psi.bc == 'finite' else psi.L 27 | assert len(U_bonds) == Nbonds 28 | for n in range(N_steps): 29 | for k in [0, 1]: # even, odd 30 | for i_bond in range(k, Nbonds, 2): 31 | update_bond(psi, i_bond, U_bonds[i_bond], chi_max, eps) 32 | # done 33 | 34 | 35 | def update_bond(psi, i, U_bond, chi_max, eps): 36 | """Apply `U_bond` acting on i,j=(i+1) to `psi`.""" 37 | j = (i + 1) % psi.L 38 | # construct theta matrix 39 | theta = psi.get_theta2(i) # vL i j vR 40 | # apply U 41 | Utheta = np.tensordot(U_bond, theta, axes=([2, 3], [1, 2])) # i j [i*] [j*], vL [i] [j] vR 42 | Utheta = np.transpose(Utheta, [2, 0, 1, 3]) # vL i j vR 43 | # split and truncate 44 | Ai, Sj, Bj = split_truncate_theta(Utheta, chi_max, eps) 45 | # put back into MPS 46 | Gi = np.tensordot(np.diag(psi.Ss[i]**(-1)), Ai, axes=(1, 0)) # vL [vL*], [vL] i vC 47 | psi.Bs[i] = np.tensordot(Gi, np.diag(Sj), axes=(2, 0)) # vL i [vC], [vC] vC 48 | psi.Ss[j] = Sj # vC 49 | psi.Bs[j] = Bj # vC j vR 50 | 51 | 52 | def example_TEBD_gs_tf_ising_finite(L, g, chi_max=30): 53 | print("finite TEBD, imaginary time evolution, transverse field Ising") 54 | print("L={L:d}, g={g:.2f}".format(L=L, g=g)) 55 | from . import a_mps 56 | from . import b_model 57 | model = b_model.TFIModel(L=L, J=1., g=g, bc='finite') 58 | psi = a_mps.init_FM_MPS(model.L, model.d, model.bc) 59 | for dt in [0.1, 0.01, 0.001, 1.e-4, 1.e-5]: 60 | U_bonds = calc_U_bonds(model.H_bonds, dt) 61 | run_TEBD(psi, U_bonds, N_steps=500, chi_max=chi_max, eps=1.e-10) 62 | E = np.sum(psi.bond_expectation_value(model.H_bonds)) 63 | print("dt = {dt:.5f}: E = {E:.13f}".format(dt=dt, E=E)) 64 | print("final bond dimensions: ", psi.get_chi()) 65 | mag_x = np.sum(psi.site_expectation_value(model.sigmax)) 66 | mag_z = np.sum(psi.site_expectation_value(model.sigmaz)) 67 | print("magnetization in X = {mag_x:.5f}".format(mag_x=mag_x)) 68 | print("magnetization in Z = {mag_z:.5f}".format(mag_z=mag_z)) 69 | if L < 20: # compare to exact result 70 | from .tfi_exact import finite_gs_energy 71 | E_exact = finite_gs_energy(L, 1., g) 72 | print("Exact diagonalization: E = {E:.13f}".format(E=E_exact)) 73 | print("relative error: ", abs((E - E_exact) / E_exact)) 74 | return E, psi, model 75 | 76 | 77 | def example_TEBD_gs_tf_ising_infinite(g, chi_max=30): 78 | print("infinite TEBD, imaginary time evolution, transverse field Ising") 79 | print("g={g:.2f}".format(g=g)) 80 | from . import a_mps 81 | from . import b_model 82 | model = b_model.TFIModel(L=2, J=1., g=g, bc='infinite') 83 | psi = a_mps.init_FM_MPS(model.L, model.d, model.bc) 84 | for dt in [0.1, 0.01, 0.001, 1.e-4, 1.e-5]: 85 | U_bonds = calc_U_bonds(model.H_bonds, dt) 86 | run_TEBD(psi, U_bonds, N_steps=500, chi_max=chi_max, eps=1.e-10) 87 | E = np.mean(psi.bond_expectation_value(model.H_bonds)) 88 | print("dt = {dt:.5f}: E (per site) = {E:.13f}".format(dt=dt, E=E)) 89 | print("final bond dimensions: ", psi.get_chi()) 90 | mag_x = np.mean(psi.site_expectation_value(model.sigmax)) 91 | mag_z = np.mean(psi.site_expectation_value(model.sigmaz)) 92 | print(" = {mag_x:.5f}".format(mag_x=mag_x)) 93 | print(" = {mag_z:.5f}".format(mag_z=mag_z)) 94 | print("correlation length:", psi.correlation_length()) 95 | # compare to exact result 96 | from .tfi_exact import infinite_gs_energy 97 | E_exact = infinite_gs_energy(1., g) 98 | print("Analytic result: E (per site) = {E:.13f}".format(E=E_exact)) 99 | print("relative error: ", abs((E - E_exact) / E_exact)) 100 | return E, psi, model 101 | 102 | 103 | def example_TEBD_tf_ising_lightcone(L, g, tmax, dt, chi_max=50): 104 | print("finite TEBD, real time evolution, transverse field Ising") 105 | print("L={L:d}, g={g:.2f}, tmax={tmax:.2f}, dt={dt:.3f}".format(L=L, g=g, tmax=tmax, dt=dt)) 106 | # find ground state with TEBD or DMRG 107 | # E, psi, model = example_TEBD_gs_tf_ising_finite(L, g) 108 | from .d_dmrg import example_DMRG_tf_ising_finite 109 | E, psi, model = example_DMRG_tf_ising_finite(L, g) 110 | i0 = L // 2 111 | # apply sigmaz on site i0 112 | SzB = np.tensordot(model.sigmaz, psi.Bs[i0], axes=(1, 1)) # i [i*], vL [i] vR 113 | psi.Bs[i0] = np.transpose(SzB, [1, 0, 2]) # vL i vR 114 | U_bonds = calc_U_bonds(model.H_bonds, 1.j * dt) # (imaginary dt -> realtime evolution) 115 | S = [psi.entanglement_entropy()] 116 | Nsteps = int(tmax / dt + 0.5) 117 | for n in range(Nsteps): 118 | if abs((n * dt + 0.1) % 0.2 - 0.1) < 1.e-10: 119 | print("t = {t:.2f}, chi =".format(t=n * dt), psi.get_chi()) 120 | run_TEBD(psi, U_bonds, 1, chi_max=chi_max, eps=1.e-10) 121 | S.append(psi.entanglement_entropy()) 122 | import matplotlib.pyplot as plt 123 | plt.figure() 124 | plt.imshow(S[::-1], 125 | vmin=0., 126 | aspect='auto', 127 | interpolation='nearest', 128 | extent=(0, L - 1., -0.5 * dt, (Nsteps + 0.5) * dt)) 129 | plt.xlabel('site $i$') 130 | plt.ylabel('time $t/J$') 131 | plt.ylim(0., tmax) 132 | plt.colorbar().set_label('entropy $S$') 133 | filename = 'c_tebd_lightcone_{g:.2f}_chi_{chi_max:d}.pdf'.format(g=g, chi_max=chi_max) 134 | plt.savefig(filename) 135 | print("saved " + filename) 136 | -------------------------------------------------------------------------------- /tenpy_toycodes/a_mps.py: -------------------------------------------------------------------------------- 1 | """Toy code implementing a matrix product state.""" 2 | # Copyright (C) TeNPy Developers, GNU GPLv3 3 | 4 | import numpy as np 5 | from scipy.linalg import svd 6 | 7 | import warnings 8 | 9 | 10 | class SimpleMPS: 11 | """Simple class for a matrix product state. 12 | 13 | We index sites with `i` from 0 to L-1; bond `i` is left of site `i`. 14 | We *assume* that the state is in right-canonical form. 15 | 16 | Parameters 17 | ---------- 18 | Bs, Ss, bc: 19 | Same as attributes. 20 | 21 | Attributes 22 | ---------- 23 | Bs : list of np.Array[ndim=3] 24 | The 'matrices', in right-canonical form, one for each physical site 25 | (within the unit-cell for an infinite MPS). 26 | Each `B[i]` has legs (virtual left, physical, virtual right), in short ``vL i vR``. 27 | Ss : list of np.Array[ndim=1] 28 | The Schmidt values at each of the bonds, ``Ss[i]`` is left of ``Bs[i]``. 29 | bc : 'infinite', 'finite' 30 | Boundary conditions. 31 | L : int 32 | Number of sites (in the unit-cell for an infinite MPS). 33 | nbonds : int 34 | Number of (non-trivial) bonds: L-1 for 'finite' boundary conditions, L for 'infinite'. 35 | """ 36 | def __init__(self, Bs, Ss, bc='finite'): 37 | assert bc in ['finite', 'infinite'] 38 | self.Bs = Bs 39 | self.Ss = Ss 40 | self.bc = bc 41 | self.L = len(Bs) 42 | self.nbonds = self.L - 1 if self.bc == 'finite' else self.L 43 | 44 | def copy(self): 45 | return SimpleMPS([B.copy() for B in self.Bs], [S.copy() for S in self.Ss], self.bc) 46 | 47 | def get_theta1(self, i): 48 | """Calculate effective single-site wave function on sites i in mixed canonical form. 49 | 50 | The returned array has legs ``vL, i, vR`` (as one of the Bs). 51 | """ 52 | return np.tensordot(np.diag(self.Ss[i]), self.Bs[i], [1, 0]) # vL [vL'], [vL] i vR 53 | 54 | def get_theta2(self, i): 55 | """Calculate effective two-site wave function on sites i,j=(i+1) in mixed canonical form. 56 | 57 | The returned array has legs ``vL, i, j, vR``. 58 | """ 59 | j = (i + 1) % self.L 60 | return np.tensordot(self.get_theta1(i), self.Bs[j], [2, 0]) # vL i [vR], [vL] j vR 61 | 62 | def get_chi(self): 63 | """Return bond dimensions.""" 64 | return [self.Bs[i].shape[2] for i in range(self.nbonds)] 65 | 66 | def site_expectation_value(self, op): 67 | """Calculate expectation values of a local operator at each site.""" 68 | result = [] 69 | for i in range(self.L): 70 | theta = self.get_theta1(i) # vL i vR 71 | op_theta = np.tensordot(op, theta, axes=(1, 1)) # i [i*], vL [i] vR 72 | result.append(np.tensordot(theta.conj(), op_theta, [[0, 1, 2], [1, 0, 2]])) 73 | # [vL*] [i*] [vR*], [i] [vL] [vR] 74 | return np.real_if_close(result) 75 | 76 | def bond_expectation_value(self, op): 77 | """Calculate expectation values of a local operator at each bond.""" 78 | result = [] 79 | for i in range(self.nbonds): 80 | theta = self.get_theta2(i) # vL i j vR 81 | op_theta = np.tensordot(op[i], theta, axes=([2, 3], [1, 2])) 82 | # i j [i*] [j*], vL [i] [j] vR 83 | result.append(np.tensordot(theta.conj(), op_theta, [[0, 1, 2, 3], [2, 0, 1, 3]])) 84 | # [vL*] [i*] [j*] [vR*], [i] [j] [vL] [vR] 85 | return np.real_if_close(result) 86 | 87 | def entanglement_entropy(self): 88 | """Return the (von-Neumann) entanglement entropy for a bipartition at any of the bonds.""" 89 | bonds = range(1, self.L) if self.bc == 'finite' else range(0, self.L) 90 | result = [] 91 | for i in bonds: 92 | S = self.Ss[i].copy() 93 | S[S < 1.e-20] = 0. # 0*log(0) should give 0; avoid warning or NaN. 94 | S2 = S * S 95 | assert abs(np.linalg.norm(S) - 1.) < 1.e-13 96 | result.append(-np.sum(S2 * np.log(S2))) 97 | return np.array(result) 98 | 99 | def correlation_length(self): 100 | """Diagonalize transfer matrix to obtain the correlation length.""" 101 | from scipy.sparse.linalg import eigs 102 | if self.get_chi()[0] > 100: 103 | warnings.warn("Skip calculating correlation_length() for large chi: could take long") 104 | return -1. 105 | assert self.bc == 'infinite' # works only in the infinite case 106 | B = self.Bs[0] # vL i vR 107 | chi = B.shape[0] 108 | T = np.tensordot(B, np.conj(B), axes=(1, 1)) # vL [i] vR, vL* [i*] vR* 109 | T = np.transpose(T, [0, 2, 1, 3]) # vL vL* vR vR* 110 | for i in range(1, self.L): 111 | B = self.Bs[i] 112 | T = np.tensordot(T, B, axes=(2, 0)) # vL vL* [vR] vR*, [vL] i vR 113 | T = np.tensordot(T, np.conj(B), axes=([2, 3], [0, 1])) 114 | # vL vL* [vR*] [i] vR, [vL*] [i*] vR* 115 | T = np.reshape(T, (chi**2, chi**2)) 116 | # Obtain the 2nd largest eigenvalue 117 | eta = eigs(T, k=2, which='LM', return_eigenvectors=False, ncv=20) 118 | xi = -self.L / np.log(np.min(np.abs(eta))) 119 | if xi > 1000.: 120 | return np.inf 121 | return xi 122 | 123 | def correlation_function(self, op_i, i, op_j, j): 124 | """Correlation function between two distant operators on sites i < j. 125 | 126 | Note: calling this function in a loop over `j` is inefficient for large j >> i. 127 | The optimization is left as an exercise to the user. 128 | Hint: Re-use the partial contractions up to but excluding site `j`. 129 | """ 130 | assert i < j 131 | theta = self.get_theta1(i) # vL i vR 132 | C = np.tensordot(op_i, theta, axes=(1, 1)) # i [i*], vL [i] vR 133 | C = np.tensordot(theta.conj(), C, axes=([0, 1], [1, 0])) # [vL*] [i*] vR*, [i] [vL] vR 134 | for k in range(i + 1, j): 135 | k = k % self.L 136 | B = self.Bs[k] # vL k vR 137 | C = np.tensordot(C, B, axes=(1, 0)) # vR* [vR], [vL] k vR 138 | C = np.tensordot(B.conj(), C, axes=([0, 1], [0, 1])) # [vL*] [k*] vR*, [vR*] [k] vR 139 | j = j % self.L 140 | B = self.Bs[j] # vL k vR 141 | C = np.tensordot(C, B, axes=(1, 0)) # vR* [vR], [vL] j vR 142 | C = np.tensordot(op_j, C, axes=(1, 1)) # j [j*], vR* [j] vR 143 | C = np.tensordot(B.conj(), C, axes=([0, 1, 2], [1, 0, 2])) # [vL*] [j*] [vR*], [j] [vR*] [vR] 144 | return C 145 | 146 | 147 | def init_FM_MPS(L, d=2, bc='finite'): 148 | """Return a ferromagnetic MPS (= product state with all spins up)""" 149 | B = np.zeros([1, d, 1], dtype=float) 150 | B[0, 0, 0] = 1. 151 | S = np.ones([1], dtype=float) 152 | Bs = [B.copy() for i in range(L)] 153 | Ss = [S.copy() for i in range(L)] 154 | return SimpleMPS(Bs, Ss, bc=bc) 155 | 156 | 157 | def init_Neel_MPS(L, d=2, bc='finite'): 158 | """Return a Neel state MPS (= product state with alternating spins up down up down... )""" 159 | S = np.ones([1], dtype=float) 160 | Bs = [] 161 | for i in range(L): 162 | B = np.zeros([1, d, 1], dtype=float) 163 | if i % 2 == 0: 164 | B[0, 0, 0] = 1. 165 | else: 166 | B[0, -1, 0] = 1. 167 | Bs.append(B) 168 | Ss = [S.copy() for i in range(L)] 169 | return SimpleMPS(Bs, Ss, bc=bc) 170 | 171 | 172 | def split_truncate_theta(theta, chi_max, eps): 173 | """Split and truncate a two-site wave function in mixed canonical form. 174 | 175 | Split a two-site wave function as follows:: 176 | vL --(theta)-- vR => vL --(A)--diag(S)--(B)-- vR 177 | | | | | 178 | i j i j 179 | 180 | Afterwards, truncate in the new leg (labeled ``vC``). 181 | 182 | Parameters 183 | ---------- 184 | theta : np.Array[ndim=4] 185 | Two-site wave function in mixed canonical form, with legs ``vL, i, j, vR``. 186 | chi_max : int 187 | Maximum number of singular values to keep 188 | eps : float 189 | Discard any singular values smaller than that. 190 | 191 | Returns 192 | ------- 193 | A : np.Array[ndim=3] 194 | Left-canonical matrix on site i, with legs ``vL, i, vC`` 195 | S : np.Array[ndim=1] 196 | Singular/Schmidt values. 197 | B : np.Array[ndim=3] 198 | Right-canonical matrix on site j, with legs ``vC, j, vR`` 199 | """ 200 | chivL, dL, dR, chivR = theta.shape 201 | theta = np.reshape(theta, [chivL * dL, dR * chivR]) 202 | X, Y, Z = svd(theta, full_matrices=False) 203 | # truncate 204 | chivC = min(chi_max, np.sum(Y > eps)) 205 | assert chivC >= 1 206 | piv = np.argsort(Y)[::-1][:chivC] # keep the largest `chivC` singular values 207 | X, Y, Z = X[:, piv], Y[piv], Z[piv, :] 208 | # renormalize 209 | S = Y / np.linalg.norm(Y) # == Y/sqrt(sum(Y**2)) 210 | # split legs of X and Z 211 | A = np.reshape(X, [chivL, dL, chivC]) 212 | B = np.reshape(Z, [chivC, dR, chivR]) 213 | return A, S, B 214 | -------------------------------------------------------------------------------- /tenpy_toycodes/d_dmrg.py: -------------------------------------------------------------------------------- 1 | """Toy code implementing the density-matrix renormalization group (DMRG).""" 2 | # Copyright (C) TeNPy Developers, GNU GPLv3 3 | 4 | import numpy as np 5 | from .a_mps import split_truncate_theta 6 | import scipy.sparse 7 | from scipy.sparse.linalg import eigsh 8 | 9 | 10 | class SimpleDMRGEngine: 11 | """DMRG algorithm, implemented as class holding the necessary data. 12 | 13 | DMRG sweeps left-right-left through the system, moving the orthogonality center along. 14 | Here, we still just save right-canonical `B` tensors in `psi`, which requires taking inverses 15 | of the Schmidt values - this is bad practice, but it keeps two things simpler: 16 | - We don't need book keeping in the MPS class to keep track of the canonical form, and all the 17 | MPS methods (expectation values etc) can just *assume* that the MPS is in right-canonical form. 18 | - The generalization to the infinite case is straight forward. 19 | Note, however, that we only use the A and B tensors directly from the SVD (without taking 20 | inverses) to update the environments. The effective Hamiltonian does thus not suffer 21 | from taking the inverses of Schmidt values. 22 | 23 | Parameters 24 | ---------- 25 | psi, model, chi_max, eps: 26 | See attributes 27 | 28 | Attributes 29 | ---------- 30 | psi : SimpleMPS 31 | The current ground-state (approximation). 32 | model : 33 | The model of which the groundstate is to be calculated. Needs to have an `H_mpo`. 34 | chi_max, eps: 35 | Truncation parameters, see :func:`a_mps.split_truncate_theta`. 36 | LPs, RPs : list of np.Array[ndim=3] 37 | Left and right parts ("environments") of the effective Hamiltonian. 38 | ``LPs[i]`` is the contraction of all parts left of site `i` in the network ````, 39 | and similar ``RPs[i]`` for all parts right of site `i`. 40 | Each ``LPs[i]`` has legs ``vL wL* vL*``, ``RPs[i]`` has legs ``vR* wR* vR`` 41 | """ 42 | def __init__(self, psi, model, chi_max, eps): 43 | assert psi.L == model.L and psi.bc == model.bc # ensure compatibility 44 | self.H_mpo = model.H_mpo 45 | self.psi = psi 46 | self.LPs = [None] * psi.L 47 | self.RPs = [None] * psi.L 48 | self.chi_max = chi_max 49 | self.eps = eps 50 | # initialize left and right environment 51 | D = self.H_mpo[0].shape[0] 52 | chi = psi.Bs[0].shape[0] 53 | LP = np.zeros([chi, D, chi], dtype=float) # vL wL* vL* 54 | RP = np.zeros([chi, D, chi], dtype=float) # vR* wR* vR 55 | LP[:, 0, :] = np.eye(chi) 56 | RP[:, D - 1, :] = np.eye(chi) 57 | self.LPs[0] = LP 58 | self.RPs[-1] = RP 59 | # initialize necessary RPs 60 | for i in range(psi.L - 1, 1, -1): 61 | self.update_RP(i, psi.Bs[i]) 62 | 63 | def sweep(self): 64 | # sweep from left to right 65 | for i in range(self.psi.nbonds - 1): 66 | self.update_bond(i) 67 | # sweep from right to left 68 | for i in range(self.psi.nbonds - 1, 0, -1): 69 | E0 = self.update_bond(i) 70 | return E0 71 | 72 | def update_bond(self, i): 73 | j = (i + 1) % self.psi.L 74 | # get effective Hamiltonian 75 | Heff = SimpleHeff2(self.LPs[i], self.RPs[j], self.H_mpo[i], self.H_mpo[j]) 76 | # Diagonalize Heff, find ground state `theta` 77 | theta_guess = self.psi.get_theta2(i) 78 | E0, theta = self.diag(Heff, theta_guess) 79 | # split and truncate 80 | Ai, Sj, Bj = split_truncate_theta(theta, self.chi_max, self.eps) 81 | # put back into MPS 82 | Gi = np.tensordot(np.diag(self.psi.Ss[i]**(-1)), Ai, axes=(1, 0)) # vL [vL*], [vL] i vC 83 | self.psi.Bs[i] = np.tensordot(Gi, np.diag(Sj), axes=(2, 0)) # vL i [vC], [vC*] vC 84 | self.psi.Ss[j] = Sj # vC 85 | self.psi.Bs[j] = Bj # vC j vR 86 | self.update_LP(i, Ai) 87 | self.update_RP(j, Bj) 88 | return E0 89 | 90 | def diag(self, Heff, guess): 91 | """Diagonalize the effective hamiltonian with an initial guess.""" 92 | guess = np.reshape(guess, [Heff.shape[1]]) 93 | E, V = eigsh(Heff, k=1, which='SA', return_eigenvectors=True, v0=guess) 94 | return E, np.reshape(V[:, 0], Heff.theta_shape) 95 | # # alternatively, use custom lanczos implementation 96 | # from .lanczos import lanczos_ground_state 97 | # return lanczos_ground_state(H, guess) 98 | 99 | def update_RP(self, i, B): 100 | """Calculate RP environment right of site `i-1`. 101 | 102 | Uses RP right of `i` and the given, right-canonical `B` on site `i`.""" 103 | j = (i - 1) % self.psi.L 104 | RP = self.RPs[i] # vR* wR* vR 105 | # B has legs vL i vR 106 | Bc = B.conj() # vL* i* vR* 107 | W = self.H_mpo[i] # wL wR i i* 108 | RP = np.tensordot(B, RP, axes=(2, 0)) # vL i [vR], [vR*] wR* vR 109 | RP = np.tensordot(RP, W, axes=([1, 2], [3, 1])) # vL [i] [wR*] vR, wL [wR] i [i*] 110 | RP = np.tensordot(RP, Bc, axes=([1, 3], [2, 1])) # vL [vR] wL [i], vL* [i*] [vR*] 111 | self.RPs[j] = RP # vL wL vL* (== vR* wR* vR on site i-1) 112 | 113 | def update_LP(self, i, A): 114 | """Calculate LP environment left of site `i+1`. 115 | 116 | Uses the LP left of site `i` and the given, left-canonical `A` on site `i`.""" 117 | j = (i + 1) % self.psi.L 118 | LP = self.LPs[i] # vL wL vL* 119 | # A has legs vL i vR 120 | Ac = A.conj() # vL* i* vR* 121 | W = self.H_mpo[i] # wL wR i i* 122 | LP = np.tensordot(LP, A, axes=(2, 0)) # vL wL* [vL*], [vL] i vR 123 | LP = np.tensordot(W, LP, axes=([0, 3], [1, 2])) # [wL] wR i [i*], vL [wL*] [i] vR 124 | LP = np.tensordot(Ac, LP, axes=([0, 1], [2, 1])) # [vL*] [i*] vR*, wR [i] [vL] vR 125 | self.LPs[j] = LP # vR* wR vR (== vL wL* vL* on site i+1) 126 | 127 | 128 | class SimpleHeff2(scipy.sparse.linalg.LinearOperator): 129 | """Class for the effective Hamiltonian on two sites. 130 | 131 | To be diagonalized in `SimpleDMRGEnginge.diag` during the bond update. Looks like this:: 132 | 133 | .--vL* vR*--. 134 | | i* j* | 135 | | | | | 136 | (LP)----(W1)--(W2)----(RP) 137 | | | | | 138 | | i j | 139 | .--vL vR--. 140 | """ 141 | def __init__(self, LP, RP, W1, W2): 142 | self.LP = LP # vL wL* vL* 143 | self.RP = RP # vR* wR* vR 144 | self.W1 = W1 # wL wC i i* 145 | self.W2 = W2 # wC wR j j* 146 | chi1, chi2 = LP.shape[0], RP.shape[2] 147 | d1, d2 = W1.shape[2], W2.shape[2] 148 | self.theta_shape = (chi1, d1, d2, chi2) # vL i j vR 149 | self.shape = (chi1 * d1 * d2 * chi2, chi1 * d1 * d2 * chi2) 150 | self.dtype = W1.dtype 151 | 152 | def _matvec(self, theta): 153 | """Calculate the matrix-vecotr product |theta'> = H_eff |theta>. 154 | 155 | This function is used by :func:`scipy.sparse.linalg.eigsh` to diagonalize 156 | the effective Hamiltonian with a Lanczos method, withouth generating the full matrix. 157 | """ 158 | x = np.reshape(theta, self.theta_shape) # vL i j vR 159 | x = np.tensordot(self.LP, x, axes=(2, 0)) # vL wL* [vL*], [vL] i j vR 160 | x = np.tensordot(x, self.W1, axes=([1, 2], [0, 3])) # vL [wL*] [i] j vR, [wL] wC i [i*] 161 | x = np.tensordot(x, self.W2, axes=([3, 1], [0, 3])) # vL [j] vR [wC] i, [wC] wR j [j*] 162 | x = np.tensordot(x, self.RP, axes=([1, 3], [0, 1])) # vL [vR] i [wR] j, [vR*] [wR*] vR 163 | x = np.reshape(x, self.shape[0]) 164 | return x 165 | 166 | def _adjoint(self): 167 | """Define self as hermitian.""" 168 | return self 169 | 170 | def trace(self): 171 | """The trace of the operator. 172 | 173 | Only needed in e_tdvp.py in expm_multiply for scipy version > 1.9.0 to avoid warnings, 174 | but cheap to calculate anyways. 175 | """ 176 | return np.inner(np.trace(self.LP, axis1=0, axis2=2), # [vL] wL* [vL*] 177 | np.dot(np.trace(self.W1, axis1=2, axis2=3), # wL wC [i] [i*] 178 | np.dot(np.trace(self.W2, axis1=2, axis2=3), # wC wR [j] [j*] 179 | np.trace(self.RP, axis1=0, axis2=2)))) # [vR*] wR* [vR] 180 | 181 | 182 | def example_DMRG_tf_ising_finite(L, g, chi_max=20): 183 | print("finite DMRG, transverse field Ising") 184 | print("L={L:d}, g={g:.2f}".format(L=L, g=g)) 185 | from . import a_mps 186 | from . import b_model 187 | model = b_model.TFIModel(L=L, J=1., g=g, bc='finite') 188 | psi = a_mps.init_FM_MPS(model.L, model.d, model.bc) 189 | eng = SimpleDMRGEngine(psi, model, chi_max=chi_max, eps=1.e-10) 190 | for i in range(10): 191 | eng.sweep() 192 | E = np.sum(psi.bond_expectation_value(model.H_bonds)) 193 | print("sweep {i:2d}: E = {E:.13f}".format(i=i + 1, E=E)) 194 | print("final bond dimensions: ", psi.get_chi()) 195 | mag_x = np.sum(psi.site_expectation_value(model.sigmax)) 196 | mag_z = np.sum(psi.site_expectation_value(model.sigmaz)) 197 | print("magnetization in X = {mag_x:.5f}".format(mag_x=mag_x)) 198 | print("magnetization in Z = {mag_z:.5f}".format(mag_z=mag_z)) 199 | if L < 20: # compare to exact result 200 | from .tfi_exact import finite_gs_energy 201 | E_exact = finite_gs_energy(L, 1., g) 202 | print("Exact diagonalization: E = {E:.13f}".format(E=E_exact)) 203 | print("relative error: ", abs((E - E_exact) / E_exact)) 204 | return E, psi, model 205 | 206 | 207 | def example_DMRG_tf_ising_infinite(g, chi_max=30): 208 | print("infinite DMRG, transverse field Ising") 209 | print("g={g:.2f}".format(g=g)) 210 | from . import a_mps 211 | from . import b_model 212 | model = b_model.TFIModel(L=2, J=1., g=g, bc='infinite') 213 | psi = a_mps.init_FM_MPS(model.L, model.d, model.bc) 214 | eng = SimpleDMRGEngine(psi, model, chi_max=chi_max, eps=1.e-14) 215 | for i in range(20): 216 | eng.sweep() 217 | E = np.mean(psi.bond_expectation_value(model.H_bonds)) 218 | print("sweep {i:2d}: E (per site) = {E:.13f}".format(i=i + 1, E=E)) 219 | print("final bond dimensions: ", psi.get_chi()) 220 | mag_x = np.mean(psi.site_expectation_value(model.sigmax)) 221 | mag_z = np.mean(psi.site_expectation_value(model.sigmaz)) 222 | print(" = {mag_x:.5f}".format(mag_x=mag_x)) 223 | print(" = {mag_z:.5f}".format(mag_z=mag_z)) 224 | print("correlation length:", psi.correlation_length()) 225 | # compare to exact result 226 | from .tfi_exact import infinite_gs_energy 227 | E_exact = infinite_gs_energy(1., g) 228 | print("Analytic result: E (per site) = {E:.13f}".format(E=E_exact)) 229 | print("relative error: ", abs((E - E_exact) / E_exact)) 230 | return E, psi, model 231 | -------------------------------------------------------------------------------- /exercise_tenpy.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "74eb0695", 6 | "metadata": {}, 7 | "source": [ 8 | "# Exercises similar to `exercise_toycodes.ipynb` using TeNPy" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "id": "6c1d1329-b87c-46cc-9703-5aa6144aa292", 14 | "metadata": {}, 15 | "source": [ 16 | "Uncomment and run the cells below (removing the `#`) when running this notebook on https://colab.research.google.com.\n", 17 | "\n", 18 | "Alternatively, you can run this notebook locally with jupyter, provided that you have the `toycodes` subfolder from \n", 19 | "https://github.com/tenpy/tenpy_toycodes\n", 20 | "in the same folder as your notebook." 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": null, 26 | "id": "cd54364b-4ac1-47bf-8c41-bd51b274eeec", 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "#!pip install git+https://github.com/tenpy/tenpy_toycodes.git\n", 31 | "#!pip install git+https://github.com/tenpy/tenpy.git\n", 32 | "\n", 33 | "# use `pip uninstall tenpy-toycodes physics-tenpy` to remove them again." 34 | ] 35 | }, 36 | { 37 | "cell_type": "markdown", 38 | "id": "e7d5c720", 39 | "metadata": {}, 40 | "source": [ 41 | "You can add your code below by inserting additional cells as neccessary and running them (press Shift+Enter).\n", 42 | "\n", 43 | "**DISCLAIMER**: Like for the toy codes, we only use very small bond dimensions here. For state-of-the-art MPS calculations (especially for cylinders towards 2D), `chi` should be significantly larger, often on the order of several 1000s." 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": null, 49 | "id": "b7d6aedd", 50 | "metadata": {}, 51 | "outputs": [], 52 | "source": [ 53 | "import numpy as np\n", 54 | "import scipy\n", 55 | "import matplotlib.pyplot as plt\n", 56 | "from pprint import pprint\n", 57 | "\n", 58 | "np.set_printoptions(precision=5, suppress=True, linewidth=100)\n", 59 | "plt.rcParams['figure.dpi'] = 150" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": null, 65 | "id": "2932ad48", 66 | "metadata": {}, 67 | "outputs": [], 68 | "source": [ 69 | "import tenpy\n", 70 | "import tenpy.linalg.np_conserved as npc\n", 71 | "from tenpy.algorithms import tebd, dmrg, tdvp\n", 72 | "from tenpy.networks.site import SpinHalfSite, SpinSite, FermionSite\n", 73 | "from tenpy.networks.mps import MPS\n", 74 | "from tenpy.models.tf_ising import TFIChain\n", 75 | "\n", 76 | "tenpy.tools.misc.setup_logging(to_stdout=\"INFO\")" 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "id": "9d536bdf", 82 | "metadata": {}, 83 | "source": [ 84 | "## Overview\n", 85 | "\n", 86 | "The source code of TeNPy is at https://github.com/tenpy/tenpy/; \n", 87 | "you can find links to the documentation and the forum in the Readme there.\n", 88 | "\n", 89 | "The [**documentation**](https://tenpy.readthedocs.io) is roughly split into the \"User guide\" (upper part in the left side-bar) and the reference of all the functions and classes (lower part).\n", 90 | "\n" 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "id": "6a01aec3", 96 | "metadata": {}, 97 | "source": [ 98 | "### Exercise(s)\n", 99 | "\n", 100 | "Read the [overview](https://tenpy.readthedocs.io/en/latest/intro/overview.html) of the TeNPy documentation.\n", 101 | "\n", 102 | "Whenever you hit an example code, try to copy it here and run it.\n", 103 | "\n", 104 | "Try to modify it slightly and try to rerun it; for example try to calculate the overlap `` in the first example." 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": null, 110 | "id": "4f1b822f", 111 | "metadata": {}, 112 | "outputs": [], 113 | "source": [] 114 | }, 115 | { 116 | "cell_type": "markdown", 117 | "id": "337e0452", 118 | "metadata": {}, 119 | "source": [ 120 | "## Initializing a Model\n", 121 | "\n", 122 | "In TeNPy, the model defines the Hilbert space and local operators, and ultimately fixes whether charge conservation is used. Therefore, you should usually start with the initialization of the model.\n", 123 | "There are many predefined models in `tenpy.models`, that you can often just use.\n", 124 | "\n", 125 | "We will first initialize the transverse field Ising model. One advantage of TeNPy is that it can exploit (abelian) charge conservation for speedups, e.g. the transverse field Ising model preserves an overall spin parity. However, this requires the form \n", 126 | "$$ H = - J \\sum_{i} \\sigma^x_i \\sigma^x_{i+1} - g \\sum_{i} \\sigma^z_i \\textrm{ in TeNPy}$$\n", 127 | "compared to the form \n", 128 | "$$ H = - J \\sum_{i} \\sigma^z_i \\sigma^z_{i+1} - g \\sum_{i} \\sigma^x_i \\textrm{ (not suitable for charge conservation)}$$ you might be more familiar with, where X and Z are exchanged.\n", 129 | "\n", 130 | "In TeNPy, allmost all parmaters can be changed dynamically through options. Default parameters are written back into the dictionaries." 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": null, 136 | "id": "a4f0ecc0", 137 | "metadata": {}, 138 | "outputs": [], 139 | "source": [ 140 | "model_params = {\n", 141 | " 'L': 20,\n", 142 | " 'g': 1.0,\n", 143 | " 'bc_MPS': 'finite',\n", 144 | " 'conserve': 'best'\n", 145 | "}\n", 146 | "model = TFIChain(model_params)\n", 147 | "# you can now print the default parameters used:\n", 148 | "print(\"used parameters, including default/not specified ones:\")\n", 149 | "pprint(model_params)" 150 | ] 151 | }, 152 | { 153 | "cell_type": "markdown", 154 | "id": "9fe62909", 155 | "metadata": {}, 156 | "source": [ 157 | "Given the model, one can easily initialize a product state, e.g. for the Neel state:" 158 | ] 159 | }, 160 | { 161 | "cell_type": "code", 162 | "execution_count": null, 163 | "id": "236024c3", 164 | "metadata": {}, 165 | "outputs": [], 166 | "source": [ 167 | "p_state = ['up', 'down'] * (model.lat.N_sites//2)\n", 168 | "psi = MPS.from_product_state(model.lat.mps_sites(), p_state, bc=model.lat.bc_MPS)\n" 169 | ] 170 | }, 171 | { 172 | "cell_type": "markdown", 173 | "id": "e3f4a8cf", 174 | "metadata": {}, 175 | "source": [ 176 | "Measuring expectation values is also similar to the toycode. However, we can even specify the local operators (defined in the sites) as strings:" 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": null, 182 | "id": "adf6d283", 183 | "metadata": {}, 184 | "outputs": [], 185 | "source": [ 186 | "print(\" = \", psi.expectation_value('Sigmaz'))\n", 187 | "print(\"S = \", psi.entanglement_entropy())" 188 | ] 189 | }, 190 | { 191 | "cell_type": "markdown", 192 | "id": "017d4d67", 193 | "metadata": {}, 194 | "source": [ 195 | "### Exercise\n", 196 | "\n", 197 | "Check the [Model.bond_energies](https://tenpy.readthedocs.io/en/latest/reference/tenpy.models.model.NearestNeighborModel.html#tenpy.models.model.NearestNeighborModel.bond_energies) for the Neel state and make sure it matches what you expect.\n", 198 | "\n" 199 | ] 200 | }, 201 | { 202 | "cell_type": "code", 203 | "execution_count": null, 204 | "id": "d0ef22db", 205 | "metadata": {}, 206 | "outputs": [], 207 | "source": [ 208 | "E = model.bond_energies(psi)\n", 209 | "print(\"energy Neel:\", E)" 210 | ] 211 | }, 212 | { 213 | "cell_type": "code", 214 | "execution_count": null, 215 | "id": "99903e46", 216 | "metadata": {}, 217 | "outputs": [], 218 | "source": [] 219 | }, 220 | { 221 | "cell_type": "markdown", 222 | "id": "bd218561", 223 | "metadata": {}, 224 | "source": [ 225 | "## Running DMRG\n", 226 | "\n", 227 | "Given the model and state, running DMRG isn't hard.\n", 228 | "Again, there are many (default) parameters for fine-tuning, see [this full option list](https://tenpy.readthedocs.io/en/latest/reference/tenpy.algorithms.dmrg.TwoSiteDMRGEngine.html#cfg-config-TwoSiteDMRGEngine) for details." 229 | ] 230 | }, 231 | { 232 | "cell_type": "code", 233 | "execution_count": null, 234 | "id": "ffc67843", 235 | "metadata": {}, 236 | "outputs": [], 237 | "source": [ 238 | "p_state = ['up'] * model.lat.N_sites\n", 239 | "psi = MPS.from_product_state(model.lat.mps_sites(), p_state, bc=model.lat.bc_MPS)\n", 240 | "algorithm_params = {\n", 241 | " 'trunc_params': {\n", 242 | " 'chi_max': 30,\n", 243 | " 'svd_min': 1.e-7,\n", 244 | " },\n", 245 | " 'max_sweeps': 40,\n", 246 | "}\n", 247 | "eng = dmrg.TwoSiteDMRGEngine(psi, model, algorithm_params)\n", 248 | "E, psi = eng.run()" 249 | ] 250 | }, 251 | { 252 | "cell_type": "code", 253 | "execution_count": null, 254 | "id": "3d9130aa", 255 | "metadata": {}, 256 | "outputs": [], 257 | "source": [] 258 | }, 259 | { 260 | "cell_type": "markdown", 261 | "id": "b0eee6bf", 262 | "metadata": {}, 263 | "source": [ 264 | "### Exercise\n", 265 | "\n", 266 | "Run DMRG for `'infinite'` MPS. \n", 267 | "(You need to initialize a new model, state, and DMRG engine for this.)\n" 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": null, 273 | "id": "90538057", 274 | "metadata": {}, 275 | "outputs": [], 276 | "source": [ 277 | "from tenpy_toycodes import tfi_exact\n", 278 | "\n", 279 | "print(\"E_exact =\", tfi_exact.infinite_gs_energy(model_params['J'], model_params['g']))" 280 | ] 281 | }, 282 | { 283 | "cell_type": "code", 284 | "execution_count": null, 285 | "id": "a9503ce9", 286 | "metadata": {}, 287 | "outputs": [], 288 | "source": [] 289 | }, 290 | { 291 | "cell_type": "code", 292 | "execution_count": null, 293 | "id": "e0b86afc", 294 | "metadata": {}, 295 | "outputs": [], 296 | "source": [] 297 | }, 298 | { 299 | "cell_type": "markdown", 300 | "id": "fa17e5ea", 301 | "metadata": {}, 302 | "source": [ 303 | "### Exercise\n", 304 | "\n", 305 | "Reproduce the phase-diagram plot of the transverse field Ising model from the toy code noteboook with TeNPy.\n" 306 | ] 307 | }, 308 | { 309 | "cell_type": "code", 310 | "execution_count": null, 311 | "id": "1dfbc061", 312 | "metadata": {}, 313 | "outputs": [], 314 | "source": [] 315 | }, 316 | { 317 | "cell_type": "code", 318 | "execution_count": null, 319 | "id": "09ead35f-5675-492c-9e5d-014879d5ccf0", 320 | "metadata": {}, 321 | "outputs": [], 322 | "source": [] 323 | }, 324 | { 325 | "cell_type": "code", 326 | "execution_count": null, 327 | "id": "58e36f79-0d6a-4a48-bb9a-8eb7174eceb2", 328 | "metadata": {}, 329 | "outputs": [], 330 | "source": [] 331 | }, 332 | { 333 | "cell_type": "markdown", 334 | "id": "ad7f61d0", 335 | "metadata": {}, 336 | "source": [ 337 | "### Advanced exercises - if you're an expert and have time left ;-)\n", 338 | "\n", 339 | "These examples only scratch on the surface of what you can do with TeNPy.\n", 340 | "- There are plenty of [more examples](https://tenpy.readthedocs.io/en/latest/examples.html) in the documentation. Take a look at them!\n", 341 | "- Try to learn how to define your own model from the TeNPy documentation. Define a model for the XX Chain.\n", 342 | "- Look at the documentation how to run TEBD and TDVP and reproduce the time-evolution plot for S(t) from the toy code notebook.\n", 343 | "- Learn how to save and load data in TeNPy." 344 | ] 345 | }, 346 | { 347 | "cell_type": "code", 348 | "execution_count": null, 349 | "id": "ea502580", 350 | "metadata": {}, 351 | "outputs": [], 352 | "source": [] 353 | } 354 | ], 355 | "metadata": { 356 | "kernelspec": { 357 | "display_name": "Python 3 (ipykernel)", 358 | "language": "python", 359 | "name": "python3" 360 | }, 361 | "language_info": { 362 | "codemirror_mode": { 363 | "name": "ipython", 364 | "version": 3 365 | }, 366 | "file_extension": ".py", 367 | "mimetype": "text/x-python", 368 | "name": "python", 369 | "nbconvert_exporter": "python", 370 | "pygments_lexer": "ipython3", 371 | "version": "3.12.2" 372 | } 373 | }, 374 | "nbformat": 4, 375 | "nbformat_minor": 5 376 | } 377 | -------------------------------------------------------------------------------- /exercise_2_mps.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "2fe29049", 6 | "metadata": {}, 7 | "source": [ 8 | "# MPS and model basics\n", 9 | "\n", 10 | "In this notebook, we introduce the `SimpleMPS` class from `tenpy_toycodes/a_mps.py` \n", 11 | "and the model class from `tenpy_toycodes/b_model.py`." 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "id": "8c0a250c-dfd9-4377-bada-fb06f5e9f86c", 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "# standard imports and cosmetics\n", 22 | "\n", 23 | "import numpy as np\n", 24 | "\n", 25 | "import matplotlib.pyplot as plt\n", 26 | "\n", 27 | "np.set_printoptions(precision=5, suppress=True, linewidth=100, threshold=50)\n", 28 | "plt.rcParams['figure.dpi'] = 150" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "id": "337e0452", 34 | "metadata": {}, 35 | "source": [ 36 | "## SimpleMPS class from `tenpy_toycodes/a_mps.py`\n", 37 | "\n", 38 | "The file `tenpy_toycodes/a_mps.py` defines a `SimpleMPS` class, that provides methods for expectation values and the entanglement entropy. \n", 39 | "\n", 40 | "You can initialize an inital product state MPS with the provided functions\n", 41 | "- `init_FM_MPS` to initialize the state $\\lvert \\uparrow \\uparrow \\cdots \\uparrow \\uparrow \\rangle$, and\n", 42 | "- `init_Neel_MPS` to initialize the Neel state $\\lvert \\uparrow \\downarrow \\cdots \\uparrow \\downarrow \\rangle$" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "id": "03fb58d1-4bb9-464d-98e7-35c564d13e64", 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "import tenpy_toycodes.a_mps\n", 53 | "from tenpy_toycodes.a_mps import SimpleMPS, init_FM_MPS, init_Neel_MPS" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": null, 59 | "id": "1f2cd7eb-534e-422e-94d3-eb2abafba7ce", 60 | "metadata": {}, 61 | "outputs": [], 62 | "source": [ 63 | "psi_FM = init_FM_MPS(L=10)\n", 64 | "print(psi_FM)" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": null, 70 | "id": "a4f0ecc0", 71 | "metadata": {}, 72 | "outputs": [], 73 | "source": [ 74 | "Z = np.diag([1., -1.])\n", 75 | "\n", 76 | "print(psi_FM.site_expectation_value(Z))" 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "id": "8b77ba98", 82 | "metadata": {}, 83 | "source": [ 84 | "### Exercise: expectation values and entropy\n", 85 | "\n", 86 | "- Initialize a Neel state MPS. Print the expectation values of `Z`\n" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": null, 92 | "id": "7905b006-9232-437a-a3aa-ba75eb6bb96a", 93 | "metadata": {}, 94 | "outputs": [], 95 | "source": [] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "id": "540a2b68-6a99-4483-ac13-01762358ee01", 100 | "metadata": {}, 101 | "source": [ 102 | "- Print the entanglement entropy. What do you expect? Why do you get so many numbers, and not just one?\n", 103 | " *Tip*: read the code ;-)\n" 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": null, 109 | "id": "82da0ad4-3fd6-4f08-9c70-73da0138bbe6", 110 | "metadata": {}, 111 | "outputs": [], 112 | "source": [] 113 | }, 114 | { 115 | "cell_type": "markdown", 116 | "id": "aef5ff1e-26f2-45e5-9dbb-4113723dfec5", 117 | "metadata": {}, 118 | "source": [ 119 | "- Extract the half-chain entanglement entropy, i.e., the entropy when cutting the chain into two equal-length halves.\n" 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": null, 125 | "id": "4716bde6-4dfa-46f6-b983-65efb9bc9972", 126 | "metadata": {}, 127 | "outputs": [], 128 | "source": [] 129 | }, 130 | { 131 | "cell_type": "markdown", 132 | "id": "ca800c65-7141-469e-8b4a-fdba982bb78b", 133 | "metadata": {}, 134 | "source": [ 135 | "- Read the code of `a_mps.py` to find out how to get the correlation $\\langle \\psi| Z_1 Z_6 |\\psi \\rangle$. Try it out!" 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": null, 141 | "id": "551155dd-0003-4d52-a162-ea9156cacff0", 142 | "metadata": {}, 143 | "outputs": [], 144 | "source": [] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": null, 149 | "id": "16498b4f-f0d7-4699-89a4-8f63f8f6b59a", 150 | "metadata": {}, 151 | "outputs": [], 152 | "source": [] 153 | }, 154 | { 155 | "cell_type": "markdown", 156 | "id": "041eb47b-53e0-4c6d-8eee-f73129a58580", 157 | "metadata": {}, 158 | "source": [ 159 | "### Exercise: `init_PM_MPS()`\n", 160 | "\n", 161 | "Write a function `init_PM_MPS` to initialize the state $\\lvert \\rightarrow \\rightarrow \\cdots \\rightarrow \\rightarrow \\rangle$,\n", 162 | "where $\\lvert \\rightarrow \\rangle = \\frac{1}{\\sqrt{2}} \\big( \\lvert\\uparrow \\rangle + \\lvert\\downarrow\\rangle \\big)$ is the spin-1/2 state pointing in plus x direction.\n", 163 | "\n", 164 | "*Tip*: the code should be similar to `init_FM_MPS`." 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": null, 170 | "id": "5134600d-36b5-469c-b24d-30cde0948aa6", 171 | "metadata": {}, 172 | "outputs": [], 173 | "source": [] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": null, 178 | "id": "67325696-74e1-4b1b-98b1-43af1ef4e989", 179 | "metadata": {}, 180 | "outputs": [], 181 | "source": [] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": null, 186 | "id": "d4144def-6120-4a37-bbce-38f55489fa66", 187 | "metadata": {}, 188 | "outputs": [], 189 | "source": [] 190 | }, 191 | { 192 | "cell_type": "markdown", 193 | "id": "0966eb59", 194 | "metadata": {}, 195 | "source": [ 196 | "## Model class from `tenpy_toycodes/b_model.py`\n", 197 | "\n", 198 | "The file `tenpy_toycodes/b_model.py` defines a `TFIModel` class representing the transverse field Ising model \n", 199 | "$$H = - J \\sum_{i} Z_i Z_{i+1} - g \\sum_{i} X_i$$\n", 200 | "\n", 201 | "It provides the Hamiltonian both in the form of bond-terms `H_bonds` (as required for TEBD) and in the form of an MPO `H_mpo` (as required for DMRG).\n", 202 | "You can use `H_bonds` with `SimpleMPS.bond_expectation_values` to evalue the energy:\n" 203 | ] 204 | }, 205 | { 206 | "cell_type": "code", 207 | "execution_count": null, 208 | "id": "d439f112", 209 | "metadata": {}, 210 | "outputs": [], 211 | "source": [ 212 | "from tenpy_toycodes.b_model import TFIModel\n", 213 | "\n", 214 | "L = 10\n", 215 | "J = 1.\n", 216 | "g = 1.2\n", 217 | "model = TFIModel(L=L, J=J, g=g, bc='finite')\n", 218 | "\n", 219 | "print(\" = \", psi_FM.bond_expectation_value(model.H_bonds))\n", 220 | "print(\"energy:\", np.sum(psi_FM.bond_expectation_value(model.H_bonds)))\n", 221 | "# (make sure the model and state have the same length and boundary conditions!)\n", 222 | "\n", 223 | "print(\"should be\", (L-1)* (-J) * (1. * 1.) + L * (-g) * (0.) )" 224 | ] 225 | }, 226 | { 227 | "cell_type": "markdown", 228 | "id": "017d4d67", 229 | "metadata": {}, 230 | "source": [ 231 | "### Exercise\n", 232 | "\n", 233 | "- Find the code where the MPO `W` for this model is defined to be\n", 234 | "$W = \\begin{pmatrix} \\mathbb{1} & \\sigma^Z & -g \\sigma^X \\\\ & & -J \\sigma^Z \\\\ & & \\mathbb{1} \\end{pmatrix}$\n", 235 | "\n" 236 | ] 237 | }, 238 | { 239 | "cell_type": "code", 240 | "execution_count": null, 241 | "id": "916858f6-bdd6-4eee-b3ac-9ee8de19f5d4", 242 | "metadata": {}, 243 | "outputs": [], 244 | "source": [] 245 | }, 246 | { 247 | "cell_type": "markdown", 248 | "id": "1111dfbd-cbae-4f6d-9ead-70edc7ac1391", 249 | "metadata": {}, 250 | "source": [ 251 | "- Check the energies for the other initial states and make sure it matches what you expect." 252 | ] 253 | }, 254 | { 255 | "cell_type": "code", 256 | "execution_count": null, 257 | "id": "b3c564f8-ef0e-444d-a0fa-c64bd5cde49c", 258 | "metadata": {}, 259 | "outputs": [], 260 | "source": [] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": null, 265 | "id": "9b4fdc65-077e-4a1d-bea0-0e1a1eff44d6", 266 | "metadata": {}, 267 | "outputs": [], 268 | "source": [] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": null, 273 | "id": "0c91a163-8545-4478-b6af-ac26c617c62d", 274 | "metadata": {}, 275 | "outputs": [], 276 | "source": [] 277 | }, 278 | { 279 | "cell_type": "markdown", 280 | "id": "84bfd922-7e85-459c-a564-742d1ff36a72", 281 | "metadata": {}, 282 | "source": [ 283 | "### Exercises (optional, if time left - for the experts, and those who want to become them)\n", 284 | "\n", 285 | "- Write an optimized function `correlation_function_all_j(psi, op_i, i, op_j, max_j)` that returns\n", 286 | " the same values as the following, naive snippet:\n", 287 | " ```\n", 288 | " results = []\n", 289 | " for j in range(i+1, max_j):\n", 290 | " results.append(psi.correlation_function(op_i, i, op_j, j)\n", 291 | " return results\n", 292 | " ```\n", 293 | " This snippet is $\\mathcal{O}(L^2)$: for each `j` it calls the `correlation_function`, \n", 294 | " which internally also has an $\\mathcal{O}(L)$ loop for the contractions. \n", 295 | " You can get this down to a single $\\mathcal{O}(L)$ loop, if you identify and reuse the parts that are the same in the diagrams for the contractions.\n" 296 | ] 297 | }, 298 | { 299 | "cell_type": "code", 300 | "execution_count": null, 301 | "id": "8d0abb36-65cf-4537-b46a-b4634175ad1a", 302 | "metadata": {}, 303 | "outputs": [], 304 | "source": [] 305 | }, 306 | { 307 | "cell_type": "markdown", 308 | "id": "3967fdd5-ac38-4545-b22a-7888ba4e90e1", 309 | "metadata": {}, 310 | "source": [ 311 | "\n", 312 | "- For benchmarks, it's useful to consider the XX chain in a staggered field, given by the Hamiltonian\n", 313 | " $$ H = \\sum_{i=0}^{N-2} (\\sigma^x_i \\sigma^x_{i+1} + \\sigma^y_i \\sigma^y_{i+1}) - h_s \\sum_{i=0}^{N-1} (-1)^i \\sigma^z_i \n", 314 | " = 2 \\sum_{i=0}^{N-2} (\\sigma^+_i \\sigma^-_{i+1} + \\sigma^+_i \\sigma^-_{i+1}) - h_s \\sum_{i=0}^{N-1} (-1)^i \\sigma^z_i\n", 315 | " $$\n", 316 | " for the usual Pauli matrices $\\sigma^x, \\sigma^y, \\sigma^z$.\n", 317 | "\n", 318 | " A Jordan-Wigner transformation maps the XX Chain to free fermions, \n", 319 | " which we can diagonalize exactly with a few lines of python codes that are given in `tenpy_toycodes/free_fermions_exact.py`." 320 | ] 321 | }, 322 | { 323 | "cell_type": "code", 324 | "execution_count": null, 325 | "id": "1e3ba6c4-51ba-4b9b-99af-d243b75b7970", 326 | "metadata": {}, 327 | "outputs": [], 328 | "source": [ 329 | "from tenpy_toycodes.free_fermions_exact import XX_model_ground_state_energy\n", 330 | "\n", 331 | "print(\"E_exact = \", XX_model_ground_state_energy(L=10, h_staggered=0.))" 332 | ] 333 | }, 334 | { 335 | "cell_type": "markdown", 336 | "id": "8438c5a0-efd6-4b54-8268-a15b1b583bc0", 337 | "metadata": {}, 338 | "source": [ 339 | "The following code implements the model for the XX Chain hamiltonian, but the MPO lacks some terms. Fill them in!\n", 340 | "\n", 341 | "\n", 342 | "Tip: In Python, `(-1)**i` represents $(-1)^i$.\n", 343 | "\n", 344 | "Tip: For the Hamiltonian $$H = \\sum_{i=0}^{N-2} (\\sigma^x_i \\sigma^x_{i+1} + \\sigma^y_i \\sigma^y_{i+1} + \\sigma^z_i \\sigma^z_{i+1}) - h \\sum_{i=0}^{N-1} \\sigma^z_i, $$ a possible MPO matrix W looks like\n", 345 | "$$ W = \\begin{pmatrix}\n", 346 | "1 & \\sigma^x & \\sigma^y & \\sigma^z & -h \\sigma^z \\\\\n", 347 | "0 & 0 & 0 & 0 & \\sigma^x \\\\ \n", 348 | "0 & 0 & 0 & 0 & \\sigma^y \\\\\n", 349 | "0 & 0 & 0 & 0 & \\sigma^z \\\\\n", 350 | "0 & 0 & 0 & 0 & 1 \n", 351 | "\\end{pmatrix} .$$\n", 352 | "Which parts do we need here?\n", 353 | "\n", 354 | "Compare the energies of different states to check that you got it correct.\n", 355 | "To really test it well, you should also check some states with non-trivial entanglement, e.g. ground states as obtained by DMRG (obtained as discussed in the next exercise notebook), for which you can directly compare to the `XX_model_ground_state_energy`." 356 | ] 357 | }, 358 | { 359 | "cell_type": "code", 360 | "execution_count": null, 361 | "id": "08a58430-a335-4044-90c9-715e6279266b", 362 | "metadata": {}, 363 | "outputs": [], 364 | "source": [ 365 | "class XXChain:\n", 366 | " \"\"\"Simple class generating the Hamiltonian of the \n", 367 | " The Hamiltonian reads\n", 368 | " .. math ::\n", 369 | " H = - J \\\\sum_{i} \\\\sigma^x_i \\\\sigma^x_{i+1} - g \\\\sum_{i} \\\\sigma^z_i\n", 370 | " \"\"\"\n", 371 | " def __init__(self, L, hs, bc='finite'):\n", 372 | " assert bc in ['finite', 'infinite']\n", 373 | " self.L, self.d, self.bc = L, 2, bc\n", 374 | " self.hs = hs\n", 375 | " self.sigmax = np.array([[0., 1.], [1., 0.]]) # Pauli X\n", 376 | " self.sigmay = np.array([[0., -1j], [1j, 0.]]) # Pauli Y\n", 377 | " self.sigmaz = np.array([[1., 0.], [0., -1.]]) # Pauli Z\n", 378 | " self.id = np.eye(2)\n", 379 | " self.init_H_bonds()\n", 380 | " self.init_H_mpo()\n", 381 | "\n", 382 | " def init_H_bonds(self):\n", 383 | " \"\"\"Initialize `H_bonds` hamiltonian.\"\"\"\n", 384 | " sx, sy, sz, id = self.sigmax, self.sigmay, self.sigmaz, self.id\n", 385 | " d = self.d\n", 386 | " nbonds = self.L - 1 if self.bc == 'finite' else self.L\n", 387 | " H_list = []\n", 388 | " for i in range(nbonds):\n", 389 | " hL = hR = 0.5 * self.hs\n", 390 | " if self.bc == 'finite':\n", 391 | " if i == 0:\n", 392 | " hL = self.hs\n", 393 | " if i + 1 == self.L - 1:\n", 394 | " hR = self.hs\n", 395 | " H_bond = np.kron(sx, sx) + np.kron(sy, sy)\n", 396 | " H_bond = H_bond - hL * (-1)**i * np.kron(sz, id) - hR * (-1)**(i+1) * np.kron(id, sz)\n", 397 | " # H_bond has legs ``i, j, i*, j*``\n", 398 | " H_list.append(np.reshape(H_bond, [d, d, d, d]))\n", 399 | " self.H_bonds = H_list\n", 400 | "\n", 401 | " # (note: not required for TEBD)\n", 402 | " def init_H_mpo(self):\n", 403 | " \"\"\"Initialize `H_mpo` Hamiltonian.\"\"\"\n", 404 | " w_list = []\n", 405 | " for i in range(self.L):\n", 406 | " w = np.zeros((4, 4, self.d, self.d), dtype=complex)\n", 407 | " w[0, 0] = w[3, 3] = self.id\n", 408 | " \n", 409 | " raise NotImplementedError(\"add further entries here\")\n", 410 | " \n", 411 | " w_list.append(w)\n", 412 | " self.H_mpo = w_list\n", 413 | " \n", 414 | "#model = XXChain(9, 4., bc='finite')" 415 | ] 416 | }, 417 | { 418 | "cell_type": "code", 419 | "execution_count": null, 420 | "id": "5058175c-ac85-4bc9-971a-78919b3284a8", 421 | "metadata": {}, 422 | "outputs": [], 423 | "source": [] 424 | } 425 | ], 426 | "metadata": { 427 | "kernelspec": { 428 | "display_name": "Python 3 (ipykernel)", 429 | "language": "python", 430 | "name": "python3" 431 | }, 432 | "language_info": { 433 | "codemirror_mode": { 434 | "name": "ipython", 435 | "version": 3 436 | }, 437 | "file_extension": ".py", 438 | "mimetype": "text/x-python", 439 | "name": "python", 440 | "nbconvert_exporter": "python", 441 | "pygments_lexer": "ipython3", 442 | "version": "3.10.6" 443 | } 444 | }, 445 | "nbformat": 4, 446 | "nbformat_minor": 5 447 | } 448 | -------------------------------------------------------------------------------- /tenpy_toycodes/e_tdvp.py: -------------------------------------------------------------------------------- 1 | """Toy code implementing the TDVP for *finite* MPS.""" 2 | # Copyright (C) TeNPy Developers, GNU GPLv3 3 | 4 | import numpy as np 5 | import scipy.sparse.linalg 6 | from scipy.sparse.linalg import expm 7 | 8 | from .d_dmrg import SimpleHeff2 9 | from . import a_mps 10 | 11 | 12 | class SimpleTDVPEngine: 13 | """TDVP algorithm for finite systems, implemented as class holding the necessary data. 14 | 15 | Note that this class is very similar to `d_dmrg.SimpleDMRGEngine`. 16 | We could use a common base class; but to keep things maximally simple and readable, 17 | we rather duplicate the code for the `__init__`, `update_LP`, and `update_RP` methods. 18 | 19 | Also, here we generalize the sweep to temporarily change the MPS to a mixed canonical form 20 | and directly save `A` tensors in it. This means that the SimpleMPS methods (which *assume* 21 | that the tensors are all right-canonical) would give wrong results *during* the sweep; yet 22 | we recover the all-right-canonical B form on each site at the end of the sweep. 23 | 24 | Parameters 25 | ---------- 26 | psi, chi_max, eps: 27 | See attributes below. 28 | model : 29 | The model with the Hamiltonian for time evolution as `model.H_mpo`. 30 | 31 | Attributes 32 | ---------- 33 | psi : SimpleMPS 34 | The current state to be evolved. 35 | H_mpo : list of W tensors with legs ``wL wR i i*`` 36 | The Hamiltonian as an MPO. 37 | chi_max, eps: 38 | Truncation parameters, see :func:`a_mps.split_truncate_theta`. 39 | Only used when we evolve two-site wave functions! 40 | LPs, RPs : list of np.Array[ndim=3] 41 | Left and right parts ("environments") of the effective Hamiltonian. 42 | ``LPs[i]`` is the contraction of all parts left of site `i` in the network ````, 43 | and similar ``RPs[i]`` for all parts right of site `i`. 44 | Each ``LPs[i]`` has legs ``vL wL* vL*``, ``RPs[i]`` has legs ``vR* wR* vR`` 45 | """ 46 | def __init__(self, psi, model, chi_max, eps): 47 | assert psi.L == model.L and psi.bc == model.bc # ensure compatibility 48 | if psi.bc != 'finite': 49 | raise ValueError("This TDVP implementation works only for finite MPS.") 50 | self.H_mpo = model.H_mpo 51 | self.psi = psi 52 | self.LPs = [None] * psi.L 53 | self.RPs = [None] * psi.L 54 | self.chi_max = chi_max 55 | self.eps = eps 56 | # initialize left and right environment 57 | D = self.H_mpo[0].shape[0] 58 | chi = psi.Bs[0].shape[0] 59 | LP = np.zeros([chi, D, chi], dtype=float) # vL wL* vL* 60 | RP = np.zeros([chi, D, chi], dtype=float) # vR* wR* vR 61 | LP[:, 0, :] = np.eye(chi) 62 | RP[:, D - 1, :] = np.eye(chi) 63 | self.LPs[0] = LP 64 | self.RPs[-1] = RP 65 | # initialize necessary RPs 66 | for i in range(psi.L - 1, 0, -1): 67 | self.update_RP(i, psi.Bs[i]) 68 | 69 | def sweep_one_site(self, dt): 70 | """Perform one one-site TDVP sweep to evolve |psi> -> exp(-i H_mpo dt) |psi>. 71 | 72 | This does *not* grow the bond dimension of the MPS, but is strictly TDVP. 73 | """ 74 | psi = self.psi 75 | L = self.psi.L 76 | # sweep from left to right 77 | theta = self.psi.get_theta1(0) 78 | for i in range(L - 1): 79 | theta = self.evolve_one_site(i, 0.5*dt, theta) # forward 80 | Ai, theta = self.split_one_site_theta(i, theta, move_right=True) 81 | # here theta is zero-site between site i and i+1 82 | psi.Bs[i] = Ai # not in right canonical form, but expect this in right-to-left sweep 83 | self.update_LP(i, Ai) 84 | theta = self.evolve_zero_site(i, -0.5*dt, theta) # backward 85 | j = i + 1 86 | Bj = self.psi.Bs[j] 87 | theta = np.tensordot(theta, Bj, axes=(1, 0)) # vL [vL'], [vL] j vR 88 | # here theta is one-site on site j = i + 1 89 | # right boundary 90 | i = L - 1 91 | theta = self.evolve_one_site(i, dt, theta) # forward 92 | theta, Bi = self.split_one_site_theta(i, theta, move_right=False) 93 | self.psi.Bs[i] = Bi 94 | self.update_RP(i, Bi) 95 | # sweep from right to left 96 | for i in reversed(range(L - 1)): 97 | theta = self.evolve_zero_site(i, -0.5*dt, theta) # backward 98 | Ai = self.psi.Bs[i] # still in left-canonical A form from the above right-sweep! 99 | theta = np.tensordot(Ai, theta, axes=(2, 0)) # vL i [vR], [vR'] vR 100 | theta = self.evolve_one_site(i, 0.5*dt, theta) # forward 101 | theta, Bi = self.split_one_site_theta(i, theta, move_right=False) 102 | self.psi.Bs[i] = Bi 103 | self.update_RP(i, Bi) 104 | # The last `evolve_one_site` brought the tensor on site 0 in right-canonical B form, 105 | # recovering the right-canonical form on each MPS tensor (as the SimpleMPS assumes). 106 | # It splitted the very left, trivial leg off theta, 107 | # which should only have an arbitrary phase for the left, trivial singular vector, 108 | # and a singular value 1 (if the state is normalized). 109 | assert theta.shape == (1, 1) 110 | assert abs(abs(theta[0]) - 1.) < 1.e-10 111 | # To keep track of the phase, we put it back into the tensor. 112 | self.psi.Bs[0] *= theta[0, 0] 113 | 114 | def sweep_two_site(self, dt): 115 | """Perform one two-site TDVP sweep to evolve |psi> -> exp(-i H_mpo dt) |psi>. 116 | 117 | This can grow the bond dimension, but is *not* stricly TDVP. 118 | """ 119 | psi = self.psi 120 | L = self.psi.L 121 | # sweep from left to right 122 | theta = self.psi.get_theta2(0) 123 | for i in range(L - 2): 124 | j = i + 1 125 | k = i + 2 126 | Ai, S, Bj = self.evolve_split_two_site(i, 0.5*dt, theta) # forward 127 | psi.Bs[i] = Ai # not in right canonical form, but expect this in right-to-left sweep 128 | self.update_LP(i, Ai) 129 | theta = np.tensordot(np.diag(S), Bj, axes=(1, 0)) # vL [vL'], [vL] j vC 130 | # here theta is one-site on site j = i + 1 131 | theta = self.evolve_one_site(j, -0.5*dt, theta) # backward 132 | Bk = self.psi.Bs[k] 133 | theta = np.tensordot(theta, Bk, axes=(2, 0)) # vL j [vC], [vC] k vR 134 | # here theta is two-site on sites j, k = i + 1, i + 2 135 | # right boundary 136 | i = L - 2 137 | j = L - 1 138 | Ai, S, Bj = self.evolve_split_two_site(i, dt, theta) # forward 139 | theta = np.tensordot(Ai, np.diag(S), axes=(2, 0)) # vL i [vC], [vC'] vC 140 | self.psi.Bs[j] = Bj 141 | self.update_RP(j, Bj) 142 | # sweep from right to left 143 | for i in reversed(range(L - 2)): 144 | j = i + 1 145 | # here, theta is one-site on site j = i + 1 146 | theta = self.evolve_one_site(j, -0.5*dt, theta) # backward 147 | Ai = self.psi.Bs[i] # still in left-canonical A form from the above right-sweep! 148 | theta = np.tensordot(Ai, theta, axes=(2, 0)) # vL i [vR], [vR'] vR 149 | # here, theta is two-site on sites i, j = i, i + 1 150 | Ai, S, Bj = self.evolve_split_two_site(i, 0.5*dt, theta) # forward 151 | self.psi.Bs[j] = Bj 152 | self.update_RP(j, Bj) 153 | theta = np.tensordot(Ai, np.diag(S), axes=(2, 0)) # vL i vC, [vC'] vC 154 | self.psi.Bs[0] = theta # this is right-canonical, because for a finite system 155 | # the left-most virtual bond is trivial, so `theta` and `B` are the same on site 0. 156 | # So we recovered the right-canonical form on each MPS tensor (as the SimpleMPS assumes). 157 | 158 | def evolve_zero_site(self, i, dt, theta): 159 | """Evolve zero-site `theta` with SimpleHeff0 right of site `i`.""" 160 | Heff = SimpleHeff0(self.LPs[i + 1], self.RPs[i]) 161 | theta = np.reshape(theta, [Heff.shape[0]]) 162 | theta = self.expm_multiply(Heff, theta, dt) 163 | # no truncation necessary! 164 | return np.reshape(theta, Heff.theta_shape) 165 | 166 | def evolve_one_site(self, i, dt, theta): 167 | """Evolve one-site `theta` with SimpleHeff1 on site i.""" 168 | # get effective Hamiltonian 169 | Heff = SimpleHeff1(self.LPs[i], self.RPs[i], self.H_mpo[i]) 170 | theta = np.reshape(theta, [Heff.shape[0]]) 171 | theta = self.expm_multiply(Heff, theta, dt) 172 | # no truncation necessary! 173 | return np.reshape(theta, Heff.theta_shape) 174 | 175 | def evolve_split_two_site(self, i, dt, theta): 176 | """Evolve two-site `theta` with SimpleHeff2 on sites i and i + 1.""" 177 | j = i + 1 178 | # get effective Hamiltonian 179 | Heff = SimpleHeff2(self.LPs[i], self.RPs[j], self.H_mpo[i], self.H_mpo[j]) 180 | theta = np.reshape(theta, [Heff.shape[0]]) # group legs 181 | theta = self.expm_multiply(Heff, theta, dt) 182 | theta = np.reshape(theta, Heff.theta_shape) # split legs 183 | # truncation necessary! 184 | Ai, S, Bj = a_mps.split_truncate_theta(theta, self.chi_max, self.eps) 185 | self.psi.Ss[j] = S 186 | return Ai, S, Bj 187 | 188 | def split_one_site_theta(self, i, theta, move_right=True): 189 | """Split a one-site theta into `Ai, theta` (right move) or ``theta, Bi`` (left move).""" 190 | chivL, d, chivR = theta.shape 191 | if move_right: 192 | # group i to the left 193 | theta = np.reshape(theta, [chivL * d, chivR]) 194 | A, S, V = a_mps.svd(theta, full_matrices=False) # vL vC, vC, vC i vR 195 | S /= np.linalg.norm(S) 196 | self.psi.Ss[i + 1] = S 197 | chivC = len(S) # no truncation necessary! 198 | A = np.reshape(A, [chivL, d, chivC]) 199 | theta = np.tensordot(np.diag(S), V, axes=(1, 0)) # vC [vC'], [vC] vR 200 | return A, theta 201 | else: 202 | # group i to the right 203 | theta = np.reshape(theta, [chivL, d * chivR]) 204 | U, S, B = a_mps.svd(theta, full_matrices=False) # vL i vC, vC, vC vR 205 | S /= np.linalg.norm(S) 206 | self.psi.Ss[i] = S 207 | chivC = len(S) # no truncation necessary! 208 | B = np.reshape(B, [chivC, d, chivR]) 209 | theta = np.tensordot(U, np.diag(S), axes=(1, 0)) # vL [vC], [vC'] vC 210 | return theta, B 211 | 212 | def update_RP(self, i, B): 213 | """Calculate RP environment right of site `i-1`. 214 | 215 | Uses RP right of `i` and the given, right-canonical `B` on site `i`.""" 216 | j = (i - 1) % self.psi.L 217 | RP = self.RPs[i] # vR* wR* vR 218 | # B has legs vL i vR 219 | Bc = B.conj() # vL* i* vR* 220 | W = self.H_mpo[i] # wL wR i i* 221 | RP = np.tensordot(B, RP, axes=(2, 0)) # vL i [vR], [vR*] wR* vR 222 | RP = np.tensordot(RP, W, axes=([1, 2], [3, 1])) # vL [i] [wR*] vR, wL [wR] i [i*] 223 | RP = np.tensordot(RP, Bc, axes=([1, 3], [2, 1])) # vL [vR] wL [i], vL* [i*] [vR*] 224 | self.RPs[j] = RP # vL wL vL* (== vR* wR* vR on site i-1) 225 | 226 | def update_LP(self, i, A): 227 | """Calculate LP environment left of site `i+1`. 228 | 229 | Uses the LP left of site `i` and the given, left-canonical `A` on site `i`.""" 230 | j = (i + 1) % self.psi.L 231 | LP = self.LPs[i] # vL wL vL* 232 | # A has legs vL i vR 233 | Ac = A.conj() # vL* i* vR* 234 | W = self.H_mpo[i] # wL wR i i* 235 | LP = np.tensordot(LP, A, axes=(2, 0)) # vL wL* [vL*], [vL] i vR 236 | LP = np.tensordot(W, LP, axes=([0, 3], [1, 2])) # [wL] wR i [i*], vL [wL*] [i] vR 237 | LP = np.tensordot(Ac, LP, axes=([0, 1], [2, 1])) # [vL*] [i*] vR*, wR [i] [vL] vR 238 | self.LPs[j] = LP # vR* wR vR (== vL wL* vL* on site i+1) 239 | 240 | def expm_multiply(self, H, psi0, dt): 241 | from scipy.sparse.linalg import expm_multiply 242 | from packaging import version 243 | if version.parse(scipy.__version__) >= version.parse('1.9.0'): 244 | traceH = H.trace() # new argument introduced in scipy 1.9.0 245 | return expm_multiply((-1.j*dt) * H, psi0, traceA =1.j*dt*traceH) 246 | return expm_multiply((-1.j*dt) * H, psi0) 247 | # # alternatively, use custom lanczos implementation 248 | # from .lanczos import lanczos_expm_multiply 249 | # return lanczos_expm_multiply(H, psi0, dt) 250 | 251 | 252 | class SimpleHeff1(scipy.sparse.linalg.LinearOperator): 253 | """Class for the effective Hamiltonian on 1 site. 254 | 255 | Basically the same as d_dmrg.SimpleHeff2, but acts on a single site:: 256 | 257 | .--vL* vR*--. 258 | | i* | 259 | | | | 260 | (LP)----(W1)----(RP) 261 | | | | 262 | | i | 263 | .--vL vR--. 264 | """ 265 | def __init__(self, LP, RP, W1, prefactor=1.): 266 | self.LP = LP # vL wL* vL* 267 | self.RP = RP # vR* wR* vR 268 | self.W1 = W1 # wL wR i i* 269 | chi1, chi2 = LP.shape[0], RP.shape[2] 270 | d1 = W1.shape[2] 271 | self.theta_shape = (chi1, d1, chi2) # vL i vR 272 | self.shape = (chi1 * d1 * chi2, chi1 * d1 * chi2) 273 | self.dtype = W1.dtype 274 | 275 | def _matvec(self, theta): 276 | """Calculate |theta'> = H_eff |theta>.""" 277 | x = np.reshape(theta, self.theta_shape) # vL i vR 278 | x = np.tensordot(self.LP, x, axes=(2, 0)) # vL wL* [vL*], [vL] i vR 279 | x = np.tensordot(x, self.W1, axes=([1, 2], [0, 3])) # vL [wL*] [i] vR, [wL] wR i [i*] 280 | x = np.tensordot(x, self.RP, axes=([1, 2], [0, 1])) # vL [vR] [wR] i, [vR*] [wR*] vR 281 | x = np.reshape(x, self.shape[0]) 282 | return x 283 | 284 | def _adjoint(self): 285 | """Define self as hermitian.""" 286 | return self 287 | 288 | def trace(self): 289 | """The trace of the operator. 290 | 291 | Only needed for expm_multiply in scipy version > 1.9.0 to avoid warnings, 292 | but cheap to calculate anyways. 293 | """ 294 | return np.inner(np.trace(self.LP, axis1=0, axis2=2), # [vL] wL* [vL*] 295 | np.dot(np.trace(self.W1, axis1=2, axis2=3), # wL wR [i] [i*] 296 | np.trace(self.RP, axis1=0, axis2=2))) # [vR*] wR* [vR] 297 | 298 | 299 | class SimpleHeff0(scipy.sparse.linalg.LinearOperator): 300 | """Class for the effective Hamiltonian. 301 | 302 | Basically the same as d_dmrg.SimpleHeff1, but acts on the zero-site wave function:: 303 | 304 | .--vL* vR*--. 305 | | | 306 | | | 307 | (LP)----------(RP) 308 | | | 309 | | | 310 | .--vL vR--. 311 | """ 312 | def __init__(self, LP, RP, prefactor=1.): 313 | self.LP = LP # vL wL* vL* 314 | self.RP = RP # vR* wR* vR 315 | chi1, chi2 = LP.shape[0], RP.shape[2] 316 | self.theta_shape = (chi1, chi2) # vL vR 317 | self.shape = (chi1 * chi2, chi1 * chi2) 318 | self.dtype = LP.dtype 319 | 320 | def _matvec(self, theta): 321 | """Calculate |theta'> = H_eff |theta>.""" 322 | x = np.reshape(theta, self.theta_shape) # vL vR 323 | x = np.tensordot(self.LP, x, axes=(2, 0)) # vL wL* [vL*], [vL] vR 324 | x = np.tensordot(x, self.RP, axes=([1, 2], [1, 0])) # vL [wL*] [vL*] , [vR*] [wR*] vR 325 | x = np.reshape(x, self.shape[0]) 326 | return x 327 | 328 | def _adjoint(self): 329 | """Define self as hermitian.""" 330 | return self 331 | 332 | def trace(self): 333 | """The trace of the operator. 334 | 335 | Only needed for expm_multiply in scipy version > 1.9.0 to avoid warnings, 336 | but cheap to calculate anyways. 337 | """ 338 | return np.inner(np.trace(self.LP, axis1=0, axis2=2), # [vL] wL* [vL*] 339 | np.trace(self.RP, axis1=0, axis2=2)) # [vR*] wR* [vR] 340 | 341 | 342 | def example_TDVP_tf_ising_lightcone(L, g, tmax, dt, one_site=True, chi_max=50): 343 | # compare this code to c_tebd.example_TEBD_tf_ising_lightcone - it's almost the same. 344 | print("finite TEBD, real time evolution, transverse field Ising") 345 | print("L={L:d}, g={g:.2f}, tmax={tmax:.2f}, dt={dt:.3f}".format(L=L, g=g, tmax=tmax, dt=dt)) 346 | # find ground state with TEBD or DMRG 347 | # E, psi, model = example_TEBD_gs_tf_ising_finite(L, g) 348 | from .d_dmrg import example_DMRG_tf_ising_finite 349 | E, psi, model = example_DMRG_tf_ising_finite(L, g) 350 | i0 = L // 2 351 | # apply sigmax on site i0 352 | SxB = np.tensordot(model.sigmaz, psi.Bs[i0], axes=(1, 1)) # i [i*], vL [i] vR 353 | psi.Bs[i0] = np.transpose(SxB, [1, 0, 2]) # vL i vR 354 | E = np.sum(psi.bond_expectation_value(model.H_bonds)) 355 | print("E after applying Sz = {E:.13f}".format(E=E)) 356 | eng = SimpleTDVPEngine(psi, model, chi_max=chi_max, eps=1.e-7) 357 | S = [psi.entanglement_entropy()] 358 | Nsteps = int(tmax / dt + 0.5) 359 | for n in range(Nsteps): 360 | if abs((n * dt + 0.1) % 0.2 - 0.1) < 1.e-10: 361 | print("t = {t:.2f}, chi =".format(t=n * dt), psi.get_chi()) 362 | if one_site: 363 | eng.sweep_one_site(dt) 364 | else: 365 | eng.sweep_two_site(dt) 366 | S.append(psi.entanglement_entropy()) 367 | import matplotlib.pyplot as plt 368 | plt.figure() 369 | plt.imshow(S[::-1], 370 | vmin=0., 371 | aspect='auto', 372 | interpolation='nearest', 373 | extent=(0, L - 1., -0.5 * dt, (Nsteps + 0.5) * dt)) 374 | plt.xlabel('site $i$') 375 | plt.ylabel('time $t/J$') 376 | plt.ylim(0., tmax) 377 | plt.colorbar().set_label('entropy $S$') 378 | E = np.sum(psi.bond_expectation_value(model.H_bonds)) 379 | print("final E = {E:.13f}".format(E=E)) 380 | -------------------------------------------------------------------------------- /solution_2_mps.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "2fe29049", 6 | "metadata": {}, 7 | "source": [ 8 | "# Solutions to MPS and model basics\n", 9 | "\n", 10 | "In this notebook, we introduce the `SimpleMPS` class from `tenpy_toycodes/a_mps.py` \n", 11 | "and the model class from `tenpy_toycodes/b_model.py`." 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 1, 17 | "id": "8c0a250c-dfd9-4377-bada-fb06f5e9f86c", 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "# standard imports and cosmetics\n", 22 | "\n", 23 | "import numpy as np\n", 24 | "\n", 25 | "import matplotlib.pyplot as plt\n", 26 | "\n", 27 | "np.set_printoptions(precision=5, suppress=True, linewidth=100, threshold=50)\n", 28 | "plt.rcParams['figure.dpi'] = 150" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "id": "337e0452", 34 | "metadata": {}, 35 | "source": [ 36 | "## SimpleMPS class from `tenpy_toycodes/a_mps.py`\n", 37 | "\n", 38 | "The file `tenpy_toycodes/a_mps.py` defines a `SimpleMPS` class, that provides methods for expectation values and the entanglement entropy. \n", 39 | "\n", 40 | "You can initialize an inital product state MPS with the provided functions\n", 41 | "- `init_FM_MPS` to initialize the state $\\lvert \\uparrow \\uparrow \\cdots \\uparrow \\uparrow \\rangle$, and\n", 42 | "- `init_Neel_MPS` to initialize the Neel state $\\lvert \\uparrow \\downarrow \\cdots \\uparrow \\downarrow \\rangle$" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 2, 48 | "id": "03fb58d1-4bb9-464d-98e7-35c564d13e64", 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "import tenpy_toycodes.a_mps\n", 53 | "from tenpy_toycodes.a_mps import SimpleMPS, init_FM_MPS, init_Neel_MPS" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 3, 59 | "id": "1f2cd7eb-534e-422e-94d3-eb2abafba7ce", 60 | "metadata": {}, 61 | "outputs": [ 62 | { 63 | "name": "stdout", 64 | "output_type": "stream", 65 | "text": [ 66 | "\n" 67 | ] 68 | } 69 | ], 70 | "source": [ 71 | "psi_FM = init_FM_MPS(L=10)\n", 72 | "print(psi_FM)" 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": 4, 78 | "id": "a4f0ecc0", 79 | "metadata": {}, 80 | "outputs": [ 81 | { 82 | "name": "stdout", 83 | "output_type": "stream", 84 | "text": [ 85 | "[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n" 86 | ] 87 | } 88 | ], 89 | "source": [ 90 | "Z = np.diag([1., -1.])\n", 91 | "\n", 92 | "print(psi_FM.site_expectation_value(Z))" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "id": "8b77ba98", 98 | "metadata": {}, 99 | "source": [ 100 | "### Exercise: expectation values and entropy\n", 101 | "\n", 102 | "- Initialize a Neel state MPS. Print the expectation values of `Z`\n" 103 | ] 104 | }, 105 | { 106 | "cell_type": "code", 107 | "execution_count": 5, 108 | "id": "7905b006-9232-437a-a3aa-ba75eb6bb96a", 109 | "metadata": {}, 110 | "outputs": [ 111 | { 112 | "name": "stdout", 113 | "output_type": "stream", 114 | "text": [ 115 | "[ 1. -1. 1. -1. 1. -1. 1. -1. 1. -1.]\n" 116 | ] 117 | } 118 | ], 119 | "source": [ 120 | "psi_Neel = init_Neel_MPS(L=10)\n", 121 | "print(psi_Neel.site_expectation_value(Z))" 122 | ] 123 | }, 124 | { 125 | "cell_type": "markdown", 126 | "id": "540a2b68-6a99-4483-ac13-01762358ee01", 127 | "metadata": {}, 128 | "source": [ 129 | "- Print the entanglement entropy. What do you expect? Why do you get so many numbers, and not just one?\n", 130 | " *Tip*: read the code ;-)\n" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": 6, 136 | "id": "82da0ad4-3fd6-4f08-9c70-73da0138bbe6", 137 | "metadata": {}, 138 | "outputs": [ 139 | { 140 | "name": "stdout", 141 | "output_type": "stream", 142 | "text": [ 143 | "[-0. -0. -0. -0. -0. -0. -0. -0. -0.]\n" 144 | ] 145 | } 146 | ], 147 | "source": [ 148 | "print(psi_Neel.entanglement_entropy()) \n", 149 | "# one value for cutting at each bond!" 150 | ] 151 | }, 152 | { 153 | "cell_type": "markdown", 154 | "id": "aef5ff1e-26f2-45e5-9dbb-4113723dfec5", 155 | "metadata": {}, 156 | "source": [ 157 | "- Extract the half-chain entanglement entropy, i.e., the entropy when cutting the chain into two equal-length halves.\n" 158 | ] 159 | }, 160 | { 161 | "cell_type": "code", 162 | "execution_count": 7, 163 | "id": "4716bde6-4dfa-46f6-b983-65efb9bc9972", 164 | "metadata": {}, 165 | "outputs": [ 166 | { 167 | "name": "stdout", 168 | "output_type": "stream", 169 | "text": [ 170 | "half-chain entropy: -0.0\n" 171 | ] 172 | } 173 | ], 174 | "source": [ 175 | "print(\"half-chain entropy: \", psi_Neel.entanglement_entropy()[(psi_Neel.L - 1)//2])\n", 176 | "# note: (L-1)//2, since we get L-1 values returned by entanglement_entropy()\n", 177 | "# a product state has no entanglement!" 178 | ] 179 | }, 180 | { 181 | "cell_type": "markdown", 182 | "id": "ca800c65-7141-469e-8b4a-fdba982bb78b", 183 | "metadata": {}, 184 | "source": [ 185 | "- Read the code of `a_mps.py` to find out how to get the correlation $\\langle \\psi| Z_1 Z_6 |\\psi \\rangle$. Try it out!" 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": 8, 191 | "id": "551155dd-0003-4d52-a162-ea9156cacff0", 192 | "metadata": {}, 193 | "outputs": [ 194 | { 195 | "name": "stdout", 196 | "output_type": "stream", 197 | "text": [ 198 | " = -1.0\n" 199 | ] 200 | } 201 | ], 202 | "source": [ 203 | "print(\" = \", psi_Neel.correlation_function(Z, 1, Z, 6))" 204 | ] 205 | }, 206 | { 207 | "cell_type": "code", 208 | "execution_count": null, 209 | "id": "16498b4f-f0d7-4699-89a4-8f63f8f6b59a", 210 | "metadata": {}, 211 | "outputs": [], 212 | "source": [] 213 | }, 214 | { 215 | "cell_type": "markdown", 216 | "id": "041eb47b-53e0-4c6d-8eee-f73129a58580", 217 | "metadata": {}, 218 | "source": [ 219 | "### Exercise: `init_PM_MPS()`\n", 220 | "\n", 221 | "Write a function `init_PM_MPS` to initialize the state $\\lvert \\rightarrow \\rightarrow \\cdots \\rightarrow \\rightarrow \\rangle$,\n", 222 | "where $\\lvert \\rightarrow \\rangle = \\frac{1}{\\sqrt{2}} \\big( \\lvert\\uparrow \\rangle + \\lvert\\downarrow\\rangle \\big)$ is the spin-1/2 state pointing in plus x direction.\n", 223 | "\n", 224 | "*Tip*: the code should be similar to `init_FM_MPS`." 225 | ] 226 | }, 227 | { 228 | "cell_type": "code", 229 | "execution_count": 9, 230 | "id": "5134600d-36b5-469c-b24d-30cde0948aa6", 231 | "metadata": {}, 232 | "outputs": [], 233 | "source": [ 234 | "def init_PM_MPS(L, bc='finite'):\n", 235 | " \"\"\"Return a paramagnetic MPS (= product state with all spins pointing in +x direction)\"\"\"\n", 236 | " d = 2\n", 237 | " B = np.zeros([1, d, 1], dtype=float)\n", 238 | " B[0, 0, 0] = 1./np.sqrt(2)\n", 239 | " B[0, 1, 0] = 1./np.sqrt(2)\n", 240 | " S = np.ones([1], dtype=float)\n", 241 | " Bs = [B.copy() for i in range(L)]\n", 242 | " Ss = [S.copy() for i in range(L)]\n", 243 | " return SimpleMPS(Bs, Ss, bc=bc)" 244 | ] 245 | }, 246 | { 247 | "cell_type": "code", 248 | "execution_count": 10, 249 | "id": "67325696-74e1-4b1b-98b1-43af1ef4e989", 250 | "metadata": {}, 251 | "outputs": [], 252 | "source": [ 253 | "psi_PM = init_PM_MPS(L=10)" 254 | ] 255 | }, 256 | { 257 | "cell_type": "markdown", 258 | "id": "0966eb59", 259 | "metadata": {}, 260 | "source": [ 261 | "## Model class from `tenpy_toycodes/b_model.py`\n", 262 | "\n", 263 | "The file `tenpy_toycodes/b_model.py` defines a `TFIModel` class representing the transverse field Ising model \n", 264 | "$$H = - J \\sum_{i} Z_i Z_{i+1} - g \\sum_{i} X_i$$\n", 265 | "\n", 266 | "It provides the Hamiltonian both in the form of bond-terms `H_bonds` (as required for TEBD) and in the form of an MPO `H_mpo` (as required for DMRG).\n", 267 | "You can use `H_bonds` with `SimpleMPS.bond_expectation_values` to evalue the energy:\n" 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": 11, 273 | "id": "d439f112", 274 | "metadata": {}, 275 | "outputs": [ 276 | { 277 | "name": "stdout", 278 | "output_type": "stream", 279 | "text": [ 280 | " = [-1. -1. -1. -1. -1. -1. -1. -1. -1.]\n", 281 | "energy: -9.0\n", 282 | "should be -9.0\n" 283 | ] 284 | } 285 | ], 286 | "source": [ 287 | "from tenpy_toycodes.b_model import TFIModel\n", 288 | "\n", 289 | "L = 10\n", 290 | "J = 1.\n", 291 | "g = 1.2\n", 292 | "model = TFIModel(L=L, J=J, g=g, bc='finite')\n", 293 | "\n", 294 | "print(\" = \", psi_FM.bond_expectation_value(model.H_bonds))\n", 295 | "print(\"energy:\", np.sum(psi_FM.bond_expectation_value(model.H_bonds)))\n", 296 | "# (make sure the model and state have the same length and boundary conditions!)\n", 297 | "\n", 298 | "print(\"should be\", (L-1)* (-J) * (1. * 1.) + L * (-g) * (0.) )" 299 | ] 300 | }, 301 | { 302 | "cell_type": "markdown", 303 | "id": "017d4d67", 304 | "metadata": {}, 305 | "source": [ 306 | "### Exercise\n", 307 | "\n", 308 | "- Find the code where the MPO `W` for this model is defined to be\n", 309 | "$W = \\begin{pmatrix} \\mathbb{1} & \\sigma^Z & -g \\sigma^X \\\\ & & -J \\sigma^Z \\\\ & & \\mathbb{1} \\end{pmatrix}$\n", 310 | "\n" 311 | ] 312 | }, 313 | { 314 | "cell_type": "code", 315 | "execution_count": 12, 316 | "id": "916858f6-bdd6-4eee-b3ac-9ee8de19f5d4", 317 | "metadata": {}, 318 | "outputs": [], 319 | "source": [ 320 | "# the code is in the method `init_H_mpo()` of the TFIModel class" 321 | ] 322 | }, 323 | { 324 | "cell_type": "markdown", 325 | "id": "1111dfbd-cbae-4f6d-9ead-70edc7ac1391", 326 | "metadata": {}, 327 | "source": [ 328 | "- Check the energies for the other initial states and make sure it matches what you expect." 329 | ] 330 | }, 331 | { 332 | "cell_type": "code", 333 | "execution_count": 13, 334 | "id": "b3c564f8-ef0e-444d-a0fa-c64bd5cde49c", 335 | "metadata": {}, 336 | "outputs": [ 337 | { 338 | "name": "stdout", 339 | "output_type": "stream", 340 | "text": [ 341 | "energy: 9.0\n", 342 | "should be 9.0\n" 343 | ] 344 | } 345 | ], 346 | "source": [ 347 | "psi_Neel = init_Neel_MPS(L=L)\n", 348 | "print(\"energy:\", np.sum(psi_Neel.bond_expectation_value(model.H_bonds)))\n", 349 | "print(\"should be\", (L-1)* (-J) * (1. * -1.) + L * (-g) * (0.) )" 350 | ] 351 | }, 352 | { 353 | "cell_type": "code", 354 | "execution_count": 14, 355 | "id": "9b4fdc65-077e-4a1d-bea0-0e1a1eff44d6", 356 | "metadata": {}, 357 | "outputs": [ 358 | { 359 | "name": "stdout", 360 | "output_type": "stream", 361 | "text": [ 362 | "energy: -11.999999999999993\n", 363 | "should be -12.0\n" 364 | ] 365 | } 366 | ], 367 | "source": [ 368 | "psi_PM = init_PM_MPS(L=L)\n", 369 | "print(\"energy:\", np.sum(psi_PM.bond_expectation_value(model.H_bonds)))\n", 370 | "print(\"should be\", (L-1)* (-J) * (0. * 0.) + L * (-g) * (1.) )" 371 | ] 372 | }, 373 | { 374 | "cell_type": "code", 375 | "execution_count": null, 376 | "id": "0c91a163-8545-4478-b6af-ac26c617c62d", 377 | "metadata": {}, 378 | "outputs": [], 379 | "source": [] 380 | }, 381 | { 382 | "cell_type": "markdown", 383 | "id": "84bfd922-7e85-459c-a564-742d1ff36a72", 384 | "metadata": {}, 385 | "source": [ 386 | "### Exercises (optional, if time left - for the experts, and those who want to become them)\n", 387 | "\n", 388 | "- Write an optimized function `correlation_function_all_j(psi, op_i, i, op_j, max_j)` that returns\n", 389 | " the same values as the following, naive snippet:\n", 390 | " ```\n", 391 | " results = []\n", 392 | " for j in range(i+1, max_j):\n", 393 | " results.append(psi.correlation_function(op_i, i, op_j, j)\n", 394 | " return results\n", 395 | " ```\n", 396 | " This snippet is $\\mathcal{O}(L^2)$: for each `j` it calls the `correlation_function`, \n", 397 | " which internally also has an $\\mathcal{O}(L)$ loop for the contractions. \n", 398 | " You can get this down to a single $\\mathcal{O}(L)$ loop, if you identify and reuse the parts that are the same in the diagrams for the contractions.\n" 399 | ] 400 | }, 401 | { 402 | "cell_type": "code", 403 | "execution_count": null, 404 | "id": "8d0abb36-65cf-4537-b46a-b4634175ad1a", 405 | "metadata": {}, 406 | "outputs": [], 407 | "source": [] 408 | }, 409 | { 410 | "cell_type": "markdown", 411 | "id": "3967fdd5-ac38-4545-b22a-7888ba4e90e1", 412 | "metadata": {}, 413 | "source": [ 414 | "\n", 415 | "- For benchmarks, it's useful to consider the XX chain in a staggered field, given by the Hamiltonian\n", 416 | " $$ H = \\sum_{i=0}^{N-2} (\\sigma^x_i \\sigma^x_{i+1} + \\sigma^y_i \\sigma^y_{i+1}) - h_s \\sum_{i=0}^{N-1} (-1)^i \\sigma^z_i \n", 417 | " = 2 \\sum_{i=0}^{N-2} (\\sigma^+_i \\sigma^-_{i+1} + \\sigma^+_i \\sigma^-_{i+1}) - h_s \\sum_{i=0}^{N-1} (-1)^i \\sigma^z_i\n", 418 | " $$\n", 419 | " for the usual Pauli matrices $\\sigma^x, \\sigma^y, \\sigma^z$.\n", 420 | "\n", 421 | " A Jordan-Wigner transformation maps the XX Chain to free fermions, \n", 422 | " which we can diagonalize exactly with a few lines of python codes that are given in `tenpy_toycodes/free_fermions_exact.py`." 423 | ] 424 | }, 425 | { 426 | "cell_type": "code", 427 | "execution_count": 15, 428 | "id": "1e3ba6c4-51ba-4b9b-99af-d243b75b7970", 429 | "metadata": {}, 430 | "outputs": [ 431 | { 432 | "name": "stdout", 433 | "output_type": "stream", 434 | "text": [ 435 | "E_exact = -12.053348366664542\n" 436 | ] 437 | } 438 | ], 439 | "source": [ 440 | "from tenpy_toycodes.free_fermions_exact import XX_model_ground_state_energy\n", 441 | "\n", 442 | "print(\"E_exact = \", XX_model_ground_state_energy(L=10, h_staggered=0.))" 443 | ] 444 | }, 445 | { 446 | "cell_type": "markdown", 447 | "id": "8438c5a0-efd6-4b54-8268-a15b1b583bc0", 448 | "metadata": {}, 449 | "source": [ 450 | "The following code implements the model for the XX Chain hamiltonian, but the MPO lacks some terms. Fill them in!\n", 451 | "\n", 452 | "\n", 453 | "Tip: In Python, `(-1)**i` represents $(-1)^i$.\n", 454 | "\n", 455 | "Tip: For the Hamiltonian $$H = \\sum_{i=0}^{N-2} (\\sigma^x_i \\sigma^x_{i+1} + \\sigma^y_i \\sigma^y_{i+1} + \\sigma^z_i \\sigma^z_{i+1}) - h \\sum_{i=0}^{N-1} \\sigma^z_i, $$ a possible MPO matrix W looks like\n", 456 | "$$ W = \\begin{pmatrix}\n", 457 | "1 & \\sigma^x & \\sigma^y & \\sigma^z & -h \\sigma^z \\\\\n", 458 | "0 & 0 & 0 & 0 & \\sigma^x \\\\ \n", 459 | "0 & 0 & 0 & 0 & \\sigma^y \\\\\n", 460 | "0 & 0 & 0 & 0 & \\sigma^z \\\\\n", 461 | "0 & 0 & 0 & 0 & 1 \n", 462 | "\\end{pmatrix} .$$\n", 463 | "Which parts do we need here?\n", 464 | "\n", 465 | "Compare the energies of different states to check that you got it correct.\n", 466 | "To really test it well, you should also check some states with non-trivial entanglement, e.g. ground states as obtained by DMRG (obtained as discussed in the next exercise notebook), for which you can directly compare to the `XX_model_ground_state_energy`." 467 | ] 468 | }, 469 | { 470 | "cell_type": "code", 471 | "execution_count": 16, 472 | "id": "08a58430-a335-4044-90c9-715e6279266b", 473 | "metadata": {}, 474 | "outputs": [], 475 | "source": [ 476 | "class XXChain:\n", 477 | " \"\"\"Simple class generating the Hamiltonian of the \n", 478 | " The Hamiltonian reads\n", 479 | " .. math ::\n", 480 | " H = - J \\\\sum_{i} \\\\sigma^x_i \\\\sigma^x_{i+1} - g \\\\sum_{i} \\\\sigma^z_i\n", 481 | " \"\"\"\n", 482 | " def __init__(self, L, hs, bc='finite'):\n", 483 | " assert bc in ['finite', 'infinite']\n", 484 | " self.L, self.d, self.bc = L, 2, bc\n", 485 | " self.hs = hs\n", 486 | " self.sigmax = np.array([[0., 1.], [1., 0.]]) # Pauli X\n", 487 | " self.sigmay = np.array([[0., -1j], [1j, 0.]]) # Pauli Y\n", 488 | " self.sigmaz = np.array([[1., 0.], [0., -1.]]) # Pauli Z\n", 489 | " self.id = np.eye(2)\n", 490 | " self.init_H_bonds()\n", 491 | " self.init_H_mpo()\n", 492 | "\n", 493 | " def init_H_bonds(self):\n", 494 | " \"\"\"Initialize `H_bonds` hamiltonian.\"\"\"\n", 495 | " sx, sy, sz, id = self.sigmax, self.sigmay, self.sigmaz, self.id\n", 496 | " d = self.d\n", 497 | " nbonds = self.L - 1 if self.bc == 'finite' else self.L\n", 498 | " H_list = []\n", 499 | " for i in range(nbonds):\n", 500 | " hL = hR = 0.5 * self.hs\n", 501 | " if self.bc == 'finite':\n", 502 | " if i == 0:\n", 503 | " hL = self.hs\n", 504 | " if i + 1 == self.L - 1:\n", 505 | " hR = self.hs\n", 506 | " H_bond = np.kron(sx, sx) + np.kron(sy, sy)\n", 507 | " H_bond = H_bond - hL * (-1)**i * np.kron(sz, id) - hR * (-1)**(i+1) * np.kron(id, sz)\n", 508 | " # H_bond has legs ``i, j, i*, j*``\n", 509 | " H_list.append(np.reshape(H_bond, [d, d, d, d]))\n", 510 | " self.H_bonds = H_list\n", 511 | "\n", 512 | " # (note: not required for TEBD)\n", 513 | " def init_H_mpo(self):\n", 514 | " \"\"\"Initialize `H_mpo` Hamiltonian.\"\"\"\n", 515 | " w_list = []\n", 516 | " for i in range(self.L):\n", 517 | " w = np.zeros((4, 4, self.d, self.d), dtype=complex)\n", 518 | " w[0, 0] = w[3, 3] = self.id\n", 519 | " \n", 520 | " raise NotImplementedError(\"add further entries here\")\n", 521 | " \n", 522 | " w_list.append(w)\n", 523 | " self.H_mpo = w_list\n", 524 | " \n", 525 | "#model = XXChain(9, 4., bc='finite')" 526 | ] 527 | }, 528 | { 529 | "cell_type": "code", 530 | "execution_count": null, 531 | "id": "5058175c-ac85-4bc9-971a-78919b3284a8", 532 | "metadata": {}, 533 | "outputs": [], 534 | "source": [] 535 | }, 536 | { 537 | "cell_type": "code", 538 | "execution_count": null, 539 | "id": "7062537b-097b-40f8-ad43-b079660c932b", 540 | "metadata": {}, 541 | "outputs": [], 542 | "source": [] 543 | } 544 | ], 545 | "metadata": { 546 | "kernelspec": { 547 | "display_name": "Python 3 (ipykernel)", 548 | "language": "python", 549 | "name": "python3" 550 | }, 551 | "language_info": { 552 | "codemirror_mode": { 553 | "name": "ipython", 554 | "version": 3 555 | }, 556 | "file_extension": ".py", 557 | "mimetype": "text/x-python", 558 | "name": "python", 559 | "nbconvert_exporter": "python", 560 | "pygments_lexer": "ipython3", 561 | "version": "3.10.6" 562 | } 563 | }, 564 | "nbformat": 4, 565 | "nbformat_minor": 5 566 | } 567 | -------------------------------------------------------------------------------- /exercise_3_dmrg.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "2fe29049", 6 | "metadata": {}, 7 | "source": [ 8 | "# DMRG runs\n", 9 | "\n", 10 | "In this notebook, we use the `SimpleDMRGEngine` class from `tenpy_toycodes/d_dmrg.py` to run DMRG and find MPS ground states." 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": null, 16 | "id": "56956a97", 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "# standard imports and cosmetics\n", 21 | "\n", 22 | "import numpy as np\n", 23 | "\n", 24 | "import matplotlib.pyplot as plt\n", 25 | "\n", 26 | "np.set_printoptions(precision=5, suppress=True, linewidth=100, threshold=50)\n", 27 | "plt.rcParams['figure.dpi'] = 150" 28 | ] 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "id": "238d735d-7666-4181-bf50-481d8cc61adb", 33 | "metadata": {}, 34 | "source": [ 35 | "In previous notebooks, we learned how to initialize `SimpleMPS`..." 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": null, 41 | "id": "8b2cfaee", 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "from tenpy_toycodes.a_mps import SimpleMPS, init_FM_MPS, init_Neel_MPS" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": null, 51 | "id": "fd3a1a46-378d-4041-86ec-4ae992de0a7e", 52 | "metadata": {}, 53 | "outputs": [], 54 | "source": [ 55 | "L = 12" 56 | ] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "execution_count": null, 61 | "id": "a4f0ecc0", 62 | "metadata": {}, 63 | "outputs": [], 64 | "source": [ 65 | "psi_FM = init_FM_MPS(L=L, d=2, bc='finite')\n", 66 | "print(psi_FM)\n", 67 | "SigmaZ = np.diag([1., -1.])\n", 68 | "print(psi_FM.site_expectation_value(SigmaZ))" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": null, 74 | "id": "fe6d3e07-f52d-466e-913d-245e541a0ced", 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "def init_PM_MPS(L, bc='finite'):\n", 79 | " \"\"\"Return a paramagnetic MPS (= product state with all spins pointing in +x direction)\"\"\"\n", 80 | " d = 2\n", 81 | " B = np.zeros([1, d, 1], dtype=float)\n", 82 | " B[0, 0, 0] = 1./np.sqrt(2)\n", 83 | " B[0, 1, 0] = 1./np.sqrt(2)\n", 84 | " S = np.ones([1], dtype=float)\n", 85 | " Bs = [B.copy() for i in range(L)]\n", 86 | " Ss = [S.copy() for i in range(L)]\n", 87 | " return SimpleMPS(Bs, Ss, bc=bc)" 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "id": "bcb5c97c-5f8b-4c1c-94c6-d3c2041db75c", 93 | "metadata": {}, 94 | "source": [ 95 | "... and how to initialize the `TFIModel`." 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": null, 101 | "id": "d439f112", 102 | "metadata": {}, 103 | "outputs": [], 104 | "source": [ 105 | "from tenpy_toycodes.b_model import TFIModel\n", 106 | "\n", 107 | "g = 1.2\n", 108 | "model = TFIModel(L=L, J=1., g=g, bc='finite')\n", 109 | "\n", 110 | "print(\" = \", psi_FM.bond_expectation_value(model.H_bonds))\n", 111 | "print(\"energy:\", np.sum(psi_FM.bond_expectation_value(model.H_bonds)))\n", 112 | "# (make sure the model and state have the same length and boundary conditions!)" 113 | ] 114 | }, 115 | { 116 | "cell_type": "markdown", 117 | "id": "93368432-f304-4327-987c-b9c12a818c26", 118 | "metadata": {}, 119 | "source": [ 120 | "\n", 121 | "For small enough system size $L \\lesssim 16$, you can compare the energies to exact diagonalization in the full Hilbert space (which is exponentially expensive!):" 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": null, 127 | "id": "25871fc0-6b4f-4266-b78c-c25519210372", 128 | "metadata": {}, 129 | "outputs": [], 130 | "source": [ 131 | "from tenpy_toycodes.tfi_exact import finite_gs_energy\n", 132 | "\n", 133 | "if L <= 16:\n", 134 | " energy_exact = finite_gs_energy(L=L, J=1., g=g)" 135 | ] 136 | }, 137 | { 138 | "cell_type": "code", 139 | "execution_count": null, 140 | "id": "69be6188-dfad-4313-90dc-ca0be87793ad", 141 | "metadata": {}, 142 | "outputs": [], 143 | "source": [] 144 | }, 145 | { 146 | "cell_type": "markdown", 147 | "id": "bb7b318b", 148 | "metadata": {}, 149 | "source": [ 150 | "## The DMRG algorithm\n", 151 | "\n", 152 | "The file `tenpy_toycodes/d_dmrg.py` implements the DMRG algorithm.\n", 153 | "It can be called like this:" 154 | ] 155 | }, 156 | { 157 | "cell_type": "code", 158 | "execution_count": null, 159 | "id": "3196f562", 160 | "metadata": {}, 161 | "outputs": [], 162 | "source": [ 163 | "from tenpy_toycodes.d_dmrg import SimpleDMRGEngine, SimpleHeff2" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": null, 169 | "id": "ffc67843", 170 | "metadata": {}, 171 | "outputs": [], 172 | "source": [ 173 | "chi_max = 15\n", 174 | "\n", 175 | "psi = init_FM_MPS(model.L, model.d, model.bc)\n", 176 | "eng = SimpleDMRGEngine(psi, model, chi_max=chi_max, eps=1.e-10)\n", 177 | "for i in range(10):\n", 178 | " E_dmrg = eng.sweep()\n", 179 | " E = np.sum(psi.bond_expectation_value(model.H_bonds))\n", 180 | " print(\"sweep {i:2d}: E = {E:.13f}\".format(i=i + 1, E=E))\n", 181 | "print(\"final bond dimensions: \", psi.get_chi())\n", 182 | "mag_x = np.mean(psi.site_expectation_value(model.sigmax))\n", 183 | "mag_z = np.mean(psi.site_expectation_value(model.sigmaz))\n", 184 | "print(\"magnetization in X = {mag_x:.5f}\".format(mag_x=mag_x))\n", 185 | "print(\"magnetization in Z = {mag_z:.5f}\".format(mag_z=mag_z))\n", 186 | "if model.L <= 16:\n", 187 | " E_exact = finite_gs_energy(L=model.L, J=model.J, g=model.g)\n", 188 | " print(\"err in energy = {err:.3e}\".format(err=E - E_exact))" 189 | ] 190 | }, 191 | { 192 | "cell_type": "markdown", 193 | "id": "b0eee6bf", 194 | "metadata": {}, 195 | "source": [ 196 | "### Exercise: read d_dmrg.py\n", 197 | "\n", 198 | "Read the code of `tenpy_toycodes/d_dmrg.py` and try to undertstand the general structure of how it works.\n" 199 | ] 200 | }, 201 | { 202 | "cell_type": "code", 203 | "execution_count": null, 204 | "id": "97226a24-892e-493a-88f4-9394615f240a", 205 | "metadata": {}, 206 | "outputs": [], 207 | "source": [] 208 | }, 209 | { 210 | "cell_type": "markdown", 211 | "id": "2a57a415-a81d-46f1-8366-533466ff70ee", 212 | "metadata": {}, 213 | "source": [ 214 | "### Exercise: measure correlation functions\n", 215 | "\n", 216 | "Just looking at expectation values of local operators is not enough.\n", 217 | "Also measure correlation functions $\\langle Z_{L/4} Z_{3L/4} \\rangle$." 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "execution_count": null, 223 | "id": "8fc2a15b-f45d-4073-aa56-7af10cb01bdc", 224 | "metadata": {}, 225 | "outputs": [], 226 | "source": [] 227 | }, 228 | { 229 | "cell_type": "code", 230 | "execution_count": null, 231 | "id": "76d1d8ed-c18d-4a84-8faf-f5390cd7e0b4", 232 | "metadata": {}, 233 | "outputs": [], 234 | "source": [] 235 | }, 236 | { 237 | "cell_type": "markdown", 238 | "id": "4799e6ed-f16d-4189-b224-8d1e972bf14d", 239 | "metadata": {}, 240 | "source": [ 241 | "### Exercise: DMRG runs\n", 242 | "\n", 243 | "Try running DMRG for various different parameters:\n", 244 | "\n", 245 | "- Change the bond dimension `chi` and truncation threashold `eps`.\n", 246 | "- Change the system size `L`\n", 247 | "- Change the model parameter `g` (at fixed $J=1$) to both the ferromagnetic phase $g1$.\n", 249 | "- Change the initial state.\n", 250 | "\n" 251 | ] 252 | }, 253 | { 254 | "cell_type": "code", 255 | "execution_count": null, 256 | "id": "c489cca3-3b21-4602-8329-6fb9e06137ff", 257 | "metadata": {}, 258 | "outputs": [], 259 | "source": [] 260 | }, 261 | { 262 | "cell_type": "markdown", 263 | "id": "efe930da-18c3-4091-8ea9-412b562c7857", 264 | "metadata": {}, 265 | "source": [ 266 | "### Exercise: Phase diagram\n", 267 | "\n", 268 | "To map out the phase diagram, it can be convenient to define a function that just runs DMRG for a given model. Fill in the below template. Use it obtain and plot the energy, correlations and magnetizations for different $L=16, 32, 64$ against $g$." 269 | ] 270 | }, 271 | { 272 | "cell_type": "code", 273 | "execution_count": null, 274 | "id": "8157051c-7bfa-407b-a889-536e821eedf6", 275 | "metadata": {}, 276 | "outputs": [], 277 | "source": [ 278 | "def run_DMRG(model, chi_max=50):\n", 279 | " print(f\"runnning DMRG for L={model.L:d}, g={model.g:.2f}, bc={model.bc}, chi_max={chi_max:d}\")\n", 280 | " \n", 281 | " raise NotImplementedError(\"TODO: this is an Exercise!\")\n", 282 | " \n", 283 | " return psi" 284 | ] 285 | }, 286 | { 287 | "cell_type": "code", 288 | "execution_count": null, 289 | "id": "b7eb8ba4-897e-495a-a22f-dc833cb246b5", 290 | "metadata": {}, 291 | "outputs": [], 292 | "source": [ 293 | "results_all = {}\n", 294 | "# TIP: comment this out after the first run to avoid overriding your data!" 295 | ] 296 | }, 297 | { 298 | "cell_type": "code", 299 | "execution_count": null, 300 | "id": "c1d120e7-0ecc-4800-857b-f12cd3fd23e7", 301 | "metadata": {}, 302 | "outputs": [], 303 | "source": [ 304 | "L = 8\n", 305 | "\n", 306 | "mag_X = []\n", 307 | "mag_Z = []\n", 308 | "E = []\n", 309 | "corr = []\n", 310 | "max_chi = []\n", 311 | "\n", 312 | "gs = [0.1, 0.5, 0.8, 0.9, 1.0, 1.1, 1.2, 1.5]\n", 313 | "for g in gs:\n", 314 | " model = TFIModel(L=L, J=1., g=g, bc='finite')\n", 315 | " psi = run_DMRG(model)\n", 316 | " mag_X.append(np.mean(psi.site_expectation_value(model.sigmax)))\n", 317 | " mag_Z.append(np.mean(psi.site_expectation_value(model.sigmaz)))\n", 318 | " E.append(np.sum(psi.bond_expectation_value(model.H_bonds)))\n", 319 | " corr.append(psi.correlation_function(model.sigmaz, model.L//4, model.sigmaz, model.L * 3 // 4))\n", 320 | " max_chi.append(max(psi.get_chi()))\n", 321 | "\n", 322 | "results_all[L] = {\n", 323 | " 'g': gs,\n", 324 | " 'mag_X': mag_X,\n", 325 | " 'mag_Z': mag_Z,\n", 326 | " 'E': E,\n", 327 | " 'corr': corr,\n", 328 | " 'max_chi': max_chi\n", 329 | "}" 330 | ] 331 | }, 332 | { 333 | "cell_type": "code", 334 | "execution_count": null, 335 | "id": "bdc10397-269f-469d-a2c0-1bb0232dfbb0", 336 | "metadata": {}, 337 | "outputs": [], 338 | "source": [ 339 | "key = 'corr'\n", 340 | "\n", 341 | "plt.figure()\n", 342 | "for L in results_all:\n", 343 | " res_L = results_all[L]\n", 344 | " plt.plot(res_L['g'], res_L[key], marker='o', label=\"L={L:d}\".format(L=L))\n", 345 | "plt.xlabel('g')\n", 346 | "plt.ylabel(key)\n", 347 | "plt.legend(loc='best')" 348 | ] 349 | }, 350 | { 351 | "cell_type": "code", 352 | "execution_count": null, 353 | "id": "6d59b6c5-551f-4b2a-b7cd-7d8c1581de67", 354 | "metadata": {}, 355 | "outputs": [], 356 | "source": [] 357 | }, 358 | { 359 | "cell_type": "code", 360 | "execution_count": null, 361 | "id": "ea8d130a-62e8-4dd6-b3d0-68849714f444", 362 | "metadata": {}, 363 | "outputs": [], 364 | "source": [] 365 | }, 366 | { 367 | "cell_type": "code", 368 | "execution_count": null, 369 | "id": "ba570aab-8ad5-4781-927f-1c9d71906e14", 370 | "metadata": {}, 371 | "outputs": [], 372 | "source": [] 373 | }, 374 | { 375 | "cell_type": "code", 376 | "execution_count": null, 377 | "id": "ab629c07-7576-4ec5-baef-d71f20f211e4", 378 | "metadata": {}, 379 | "outputs": [], 380 | "source": [] 381 | }, 382 | { 383 | "cell_type": "markdown", 384 | "id": "b70436f7-f7c9-49a8-bdc4-c90d322681ca", 385 | "metadata": {}, 386 | "source": [ 387 | "## Infinite DMRG" 388 | ] 389 | }, 390 | { 391 | "cell_type": "markdown", 392 | "id": "78ae81b0-3ade-4133-9280-dd57b9958807", 393 | "metadata": {}, 394 | "source": [ 395 | "The given DMRG code also works with `bc='infinite'` boundary conditions of the model and state.\n", 396 | "The given `SimpleDMRG` code also allows to run infinite DMRG, simply by replacing the `bc='finite'` for both the model and the MPS. \n", 397 | "\n", 398 | "- Look at the implementation of `d_dmrg.py` (and `a_mps.py`) to see where the differences are.\n", 399 | "\n", 400 | "Again, we can compare to analytic calculations possible for the Transverse Field Ising model.\n", 401 | " " 402 | ] 403 | }, 404 | { 405 | "cell_type": "code", 406 | "execution_count": null, 407 | "id": "98e49bcb-9dc9-4dc1-aff3-f8d08fff5085", 408 | "metadata": {}, 409 | "outputs": [], 410 | "source": [ 411 | "from tenpy_toycodes.tfi_exact import infinite_gs_energy" 412 | ] 413 | }, 414 | { 415 | "cell_type": "markdown", 416 | "id": "f1ec34c0-68ac-4e4a-823c-f8d0ef7e7a63", 417 | "metadata": {}, 418 | "source": [ 419 | "\n", 420 | "The `L` parameter now just indices the number of tensors insite the unit cell of the infinite MPS.\n", 421 | "It has to be at least `2`, since we optimize 2 tensors at once in our DMRG code.\n", 422 | "Note that we now use the `mean` to calculate densities of observables instead of extensive quantities:" 423 | ] 424 | }, 425 | { 426 | "cell_type": "code", 427 | "execution_count": null, 428 | "id": "545b6a75-1550-433e-8763-968281137a41", 429 | "metadata": {}, 430 | "outputs": [], 431 | "source": [ 432 | "model = TFIModel(L=2, J=1., g=0.8, bc='infinite') # just change bc='infinite' here\n", 433 | "\n", 434 | "chi_max = 10\n", 435 | "\n", 436 | "psi = init_FM_MPS(model.L, model.d, model.bc)\n", 437 | "eng = SimpleDMRGEngine(psi, model, chi_max=chi_max, eps=1.e-7)\n", 438 | "for i in range(10):\n", 439 | " E_dmrg = eng.sweep()\n", 440 | " E = np.mean(psi.bond_expectation_value(model.H_bonds))\n", 441 | " #if i % 10 == 9:\n", 442 | " print(\"sweep {i:2d}: E/L = {E:.13f}\".format(i=i + 1, E=E))\n", 443 | "print(\"final bond dimensions: \", psi.get_chi())\n", 444 | "mag_x = np.mean(psi.site_expectation_value(model.sigmax))\n", 445 | "mag_z = np.mean(psi.site_expectation_value(model.sigmaz))\n", 446 | "print(\"magnetization density in X = {mag_x:.5f}\".format(mag_x=mag_x))\n", 447 | "print(\"magnetization density in Z = {mag_z:.5f}\".format(mag_z=mag_z))\n", 448 | "E_exact = infinite_gs_energy(model.J, model.g)\n", 449 | "print(\"err in energy = {err:.3e}\".format(err=E - E_exact))" 450 | ] 451 | }, 452 | { 453 | "cell_type": "markdown", 454 | "id": "d2ed4ee1-6b8e-448b-9a76-b2a9f2a1ef6e", 455 | "metadata": {}, 456 | "source": [ 457 | "### Exercise: Infinite DMRG\n", 458 | "\n", 459 | "Try running the infinite DMRG code. How many sweeps do you now need to perform to converge now?\n", 460 | " Does it depend on `g` and `chi`?" 461 | ] 462 | }, 463 | { 464 | "cell_type": "code", 465 | "execution_count": null, 466 | "id": "80476e8b-e84a-41b3-950e-1b378bd853ae", 467 | "metadata": {}, 468 | "outputs": [], 469 | "source": [] 470 | }, 471 | { 472 | "cell_type": "code", 473 | "execution_count": null, 474 | "id": "ca2aa6c1-19d6-4a9b-b63c-8f5706a9f6e0", 475 | "metadata": {}, 476 | "outputs": [], 477 | "source": [] 478 | }, 479 | { 480 | "cell_type": "code", 481 | "execution_count": null, 482 | "id": "292897df-7862-46ab-a1ae-1239185d9828", 483 | "metadata": {}, 484 | "outputs": [], 485 | "source": [] 486 | }, 487 | { 488 | "cell_type": "markdown", 489 | "id": "6045388b-47b8-4a5e-bc3b-bcd2dfa07be2", 490 | "metadata": {}, 491 | "source": [ 492 | "From the exercise, you should see that you need significantly more sweeps when the correlation length gets larger, in particular at the critical point! At the critical point, the [correlation length (of infinite MPS) scales](https://arxiv.org/abs/0812.2903) as $\\xi \\propto \\chi^\\kappa$, so you again need more sweeps to converge at larger bond dimensions." 493 | ] 494 | }, 495 | { 496 | "cell_type": "code", 497 | "execution_count": null, 498 | "id": "60d545ba-a00d-4461-b6db-82336a02465e", 499 | "metadata": {}, 500 | "outputs": [], 501 | "source": [] 502 | }, 503 | { 504 | "cell_type": "code", 505 | "execution_count": null, 506 | "id": "d16df3aa", 507 | "metadata": {}, 508 | "outputs": [], 509 | "source": [] 510 | }, 511 | { 512 | "cell_type": "markdown", 513 | "id": "1e99ab49", 514 | "metadata": {}, 515 | "source": [ 516 | "### Advanced exercises for the experts (and those who want to become them ;-) )\n", 517 | "\n", 518 | "- Obtain the ground state of the transverse field ising model at the critical point with DMRG for large `L`.\n", 519 | " Try to plot the corrlation function as a function of `j-i`.\n", 520 | " What form does it have? Is an MPS a good ansatz for that?\n", 521 | "\n", 522 | "\n", 523 | "\n", 524 | "\n" 525 | ] 526 | }, 527 | { 528 | "cell_type": "code", 529 | "execution_count": null, 530 | "id": "8adcb517-0981-4faa-a756-1b792a1ee00f", 531 | "metadata": {}, 532 | "outputs": [], 533 | "source": [] 534 | }, 535 | { 536 | "cell_type": "markdown", 537 | "id": "119170d8-d2bf-4d5b-b799-a08574dc7e37", 538 | "metadata": {}, 539 | "source": [ 540 | "- Compare running DMRG and imaginary time evolution with TEBD from `tenpy_toycodes/c_tebd.py` \n", 541 | " for various parameters of `L`, `J`, `g`, and `bc`. Which one is faster? Do they always agree?" 542 | ] 543 | }, 544 | { 545 | "cell_type": "code", 546 | "execution_count": null, 547 | "id": "5f1aabfc-7a3d-4450-a1fa-d94f8f2d73fe", 548 | "metadata": {}, 549 | "outputs": [], 550 | "source": [] 551 | }, 552 | { 553 | "cell_type": "code", 554 | "execution_count": null, 555 | "id": "1dfbc061", 556 | "metadata": {}, 557 | "outputs": [], 558 | "source": [] 559 | }, 560 | { 561 | "cell_type": "code", 562 | "execution_count": null, 563 | "id": "b9d25343", 564 | "metadata": {}, 565 | "outputs": [], 566 | "source": [] 567 | }, 568 | { 569 | "cell_type": "code", 570 | "execution_count": null, 571 | "id": "da649492-fe07-437f-931a-7843a3e4a6a4", 572 | "metadata": {}, 573 | "outputs": [], 574 | "source": [] 575 | }, 576 | { 577 | "cell_type": "code", 578 | "execution_count": null, 579 | "id": "97801804-24e0-4261-912f-699fc8b93228", 580 | "metadata": {}, 581 | "outputs": [], 582 | "source": [] 583 | }, 584 | { 585 | "cell_type": "code", 586 | "execution_count": null, 587 | "id": "1c5c22e8-1254-4073-86d6-8d4c68e1617b", 588 | "metadata": {}, 589 | "outputs": [], 590 | "source": [] 591 | } 592 | ], 593 | "metadata": { 594 | "kernelspec": { 595 | "display_name": "Python 3 (ipykernel)", 596 | "language": "python", 597 | "name": "python3" 598 | }, 599 | "language_info": { 600 | "codemirror_mode": { 601 | "name": "ipython", 602 | "version": 3 603 | }, 604 | "file_extension": ".py", 605 | "mimetype": "text/x-python", 606 | "name": "python", 607 | "nbconvert_exporter": "python", 608 | "pygments_lexer": "ipython3", 609 | "version": "3.10.6" 610 | } 611 | }, 612 | "nbformat": 4, 613 | "nbformat_minor": 5 614 | } 615 | -------------------------------------------------------------------------------- /exercise_toycodes.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "2fe29049", 6 | "metadata": {}, 7 | "source": [ 8 | "# Exercises based on the toy codes" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "id": "39301469", 14 | "metadata": {}, 15 | "source": [ 16 | "Uncomment and run the cells below (removing the `#`) when running this notebook on https://colab.research.google.com.\n", 17 | "\n", 18 | "Alternatively, you can run this notebook locally with jupyter, provided that you have the `toycodes` subfolder from \n", 19 | "https://github.com/tenpy/tenpy_toycodes\n", 20 | "in the same folder as your notebook." 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": null, 26 | "id": "8d42e939", 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "#!pip install git+https://github.com/tenpy/tenpy_toycodes.git\n", 31 | "\n", 32 | "# use `pip uninstall tenpy-toycodes` to remove it a gain." 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "id": "e7d5c720", 38 | "metadata": {}, 39 | "source": [ 40 | "This tutorial focuses on a set of toy codes (using only python with numpy + scipy) that provide a simple implementation of the various MPS algorithms.\n", 41 | "\n", 42 | "You can add your code below by inserting additional cells as neccessary and running them (press Shift+Enter).\n", 43 | "\n", 44 | "**DISCLAIMER**: the toy codes used are not optimized, and we only use very small bond dimensions here. For state-of-the-art MPS calculations (especially for cylinders towards 2D), `chi` should be significantly larger, often on the order of several 1000s." 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": null, 50 | "id": "56956a97", 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "execution_count": null, 58 | "id": "b7d6aedd", 59 | "metadata": {}, 60 | "outputs": [], 61 | "source": [ 62 | "import numpy as np\n", 63 | "import scipy\n", 64 | "import matplotlib.pyplot as plt\n", 65 | "\n", 66 | "np.set_printoptions(precision=5, suppress=True, linewidth=100)\n", 67 | "plt.rcParams['figure.dpi'] = 150" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "id": "337e0452", 73 | "metadata": {}, 74 | "source": [ 75 | "## toy codes: a_mps.py\n", 76 | "\n", 77 | "The file [a_mps.py](https://github.com/tenpy/tenpy_toycodes/blob/main/tenpy_toycodes/a_mps.py) defines a `SimpleMPS` class, that provides methods for expectation values and the entanglement entropy. You can initialize an inital product state MPS with the provided functions `init_FM_MPS` or `init_Neel_MPS`:" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": null, 83 | "id": "8b2cfaee", 84 | "metadata": {}, 85 | "outputs": [], 86 | "source": [ 87 | "from tenpy_toycodes.a_mps import init_FM_MPS, init_Neel_MPS, SimpleMPS" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": null, 93 | "id": "a4f0ecc0", 94 | "metadata": {}, 95 | "outputs": [], 96 | "source": [ 97 | "psi_FM = init_FM_MPS(L=10, d=2, bc='finite')\n", 98 | "print(psi_FM)\n", 99 | "SigmaZ = np.diag([1., -1.])\n", 100 | "print(psi_FM.site_expectation_value(SigmaZ))" 101 | ] 102 | }, 103 | { 104 | "cell_type": "markdown", 105 | "id": "8b77ba98", 106 | "metadata": {}, 107 | "source": [ 108 | "### Exercise\n", 109 | "Initialize a Neel state MPS. Print the `SigmaZ` expectation values.\n", 110 | "\n", 111 | "\n", 112 | "Print the entanglement entropy. What do you expect? Why do you get so many numbers, and not just one?\n", 113 | "Tipp: read the code ;-)\n", 114 | "\n", 115 | "Extract the half-chain entanglement entropy, i.e., the entropy when cutting the chain into two equal-length halves." 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": null, 121 | "id": "7905b006-9232-437a-a3aa-ba75eb6bb96a", 122 | "metadata": {}, 123 | "outputs": [], 124 | "source": [] 125 | }, 126 | { 127 | "cell_type": "markdown", 128 | "id": "041eb47b-53e0-4c6d-8eee-f73129a58580", 129 | "metadata": {}, 130 | "source": [ 131 | "### Exercise\n", 132 | "\n", 133 | "Write a function to initialize an MPS representing a product of singlets on neighboring sites.\n", 134 | "Check the expectation values of $\\sigma^z$, entropies and the correlation functions $\\sigma^z_0 \\sigma^z_1$ and $\\sigma^z_0 \\sigma^z_2$ for correctness.\n" 135 | ] 136 | }, 137 | { 138 | "cell_type": "code", 139 | "execution_count": null, 140 | "id": "5134600d-36b5-469c-b24d-30cde0948aa6", 141 | "metadata": {}, 142 | "outputs": [], 143 | "source": [] 144 | }, 145 | { 146 | "cell_type": "markdown", 147 | "id": "0966eb59", 148 | "metadata": {}, 149 | "source": [ 150 | "## toy codes: b_model.py\n", 151 | "\n", 152 | "The file [b_model.py](https://github.com/tenpy/tenpy_toycodes/blob/main/tenpy_toycodes/b_model.py) defines a `TFIModel` class representing the transverse field Ising model \n", 153 | "$$H = - J \\sum_{i} \\sigma^z_i \\sigma^z_{i+1} - g \\sum_{i} \\sigma^x_i$$\n", 154 | "\n", 155 | "\n", 156 | "that provides both in the form of bond-terms `H_bonds` (as required for TEBD) and in the form of an MPO `H_mpo`.\n", 157 | "You can use `H_bonds` with `SimpleMPS.bond_expectation_values` to evalue the energy.\n" 158 | ] 159 | }, 160 | { 161 | "cell_type": "code", 162 | "execution_count": null, 163 | "id": "d439f112", 164 | "metadata": {}, 165 | "outputs": [], 166 | "source": [ 167 | "from tenpy_toycodes.b_model import TFIModel\n", 168 | "\n", 169 | "model = TFIModel(L=10, J=1., g=1.2, bc='finite')\n", 170 | "\n", 171 | "print(\" = \", psi_FM.bond_expectation_value(model.H_bonds))\n", 172 | "print(\"energy:\", np.sum(psi_FM.bond_expectation_value(model.H_bonds)))\n", 173 | "# (make sure the model and state have the same length and boundary conditions!)" 174 | ] 175 | }, 176 | { 177 | "cell_type": "markdown", 178 | "id": "017d4d67", 179 | "metadata": {}, 180 | "source": [ 181 | "### Exercise\n", 182 | "\n", 183 | "Check the energies for the Neel state and make sure it matches what you expect." 184 | ] 185 | }, 186 | { 187 | "cell_type": "code", 188 | "execution_count": null, 189 | "id": "cb17e0cc-8138-4348-8faf-bd727a3b87ca", 190 | "metadata": {}, 191 | "outputs": [], 192 | "source": [] 193 | }, 194 | { 195 | "cell_type": "markdown", 196 | "id": "bd218561", 197 | "metadata": {}, 198 | "source": [ 199 | "## toy codes: c_tebd.py\n", 200 | "\n", 201 | "The file [c_tebd.py](https://github.com/tenpy/tenpy_toycodes/blob/main/tenpy_toycodes/c_tebd.py) implements the TEBD algorithm. \n", 202 | "\n", 203 | "You can run TEBD with imaginary time evolutinon to find the ground state like this:" 204 | ] 205 | }, 206 | { 207 | "cell_type": "code", 208 | "execution_count": null, 209 | "id": "d6c0d701", 210 | "metadata": {}, 211 | "outputs": [], 212 | "source": [ 213 | "from tenpy_toycodes.c_tebd import calc_U_bonds, run_TEBD" 214 | ] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "execution_count": null, 219 | "id": "2e8f7a57", 220 | "metadata": {}, 221 | "outputs": [], 222 | "source": [ 223 | "# assuming you defined a `model`\n", 224 | "\n", 225 | "chi_max = 15\n", 226 | "\n", 227 | "psi = init_FM_MPS(model.L, model.d, model.bc)\n", 228 | "for dt in [0.1, 0.01, 0.001, 1.e-4, 1.e-5]:\n", 229 | " U_bonds = calc_U_bonds(model.H_bonds, dt)\n", 230 | " run_TEBD(psi, U_bonds, N_steps=100, chi_max=chi_max, eps=1.e-10)\n", 231 | " E = np.sum(psi.bond_expectation_value(model.H_bonds))\n", 232 | " print(\"dt = {dt:.5f}: E = {E:.13f}\".format(dt=dt, E=E))\n", 233 | "# the `run_TEBD` modified `psi`, so we can now calculate the expectation values from it\n", 234 | "print(\"final bond dimensions: \", psi.get_chi())\n", 235 | "mag_x = np.sum(psi.site_expectation_value(model.sigmax))\n", 236 | "mag_z = np.sum(psi.site_expectation_value(model.sigmaz))\n", 237 | "print(\"magnetization in X = {mag_x:.5f}\".format(mag_x=mag_x))\n", 238 | "print(\"magnetization in Z = {mag_z:.5f}\".format(mag_z=mag_z))\n" 239 | ] 240 | }, 241 | { 242 | "cell_type": "markdown", 243 | "id": "a4785dbd-afd4-436b-ae11-178992509837", 244 | "metadata": {}, 245 | "source": [ 246 | "### Exercise\n", 247 | "\n", 248 | "Read the code of [c_tebd.py](https://github.com/tenpy/tenpy_toycodes/blob/main/tenpy_toycodes/c_tebd.py) and try to understand the general structure of how it works.\n" 249 | ] 250 | }, 251 | { 252 | "cell_type": "code", 253 | "execution_count": null, 254 | "id": "ef2b2c13-3570-41ba-ab59-4cc249911f22", 255 | "metadata": {}, 256 | "outputs": [], 257 | "source": [] 258 | }, 259 | { 260 | "cell_type": "code", 261 | "execution_count": null, 262 | "id": "ca846a61-8b27-483e-93ce-c489ac659792", 263 | "metadata": {}, 264 | "outputs": [], 265 | "source": [] 266 | }, 267 | { 268 | "cell_type": "code", 269 | "execution_count": null, 270 | "id": "c2079f84-16ef-42e6-a317-cc40b51160a7", 271 | "metadata": {}, 272 | "outputs": [], 273 | "source": [] 274 | }, 275 | { 276 | "cell_type": "markdown", 277 | "id": "bb7b318b", 278 | "metadata": {}, 279 | "source": [ 280 | "## toy codes: d_dmrg.py\n", 281 | "\n", 282 | "The file [d_dmrg.py](https://github.com/tenpy/tenpy_toycodes/blob/main/tenpy_toycodes/d_dmrg.py) implements the DMRG algorithm.\n", 283 | "It can be called like this:" 284 | ] 285 | }, 286 | { 287 | "cell_type": "code", 288 | "execution_count": null, 289 | "id": "3196f562", 290 | "metadata": {}, 291 | "outputs": [], 292 | "source": [ 293 | "from tenpy_toycodes.d_dmrg import SimpleDMRGEngine, SimpleHeff2" 294 | ] 295 | }, 296 | { 297 | "cell_type": "code", 298 | "execution_count": null, 299 | "id": "ffc67843", 300 | "metadata": {}, 301 | "outputs": [], 302 | "source": [ 303 | "chi_max = 15\n", 304 | "\n", 305 | "psi = init_FM_MPS(model.L, model.d, model.bc)\n", 306 | "eng = SimpleDMRGEngine(psi, model, chi_max=chi_max, eps=1.e-10)\n", 307 | "for i in range(10):\n", 308 | " E_dmrg = eng.sweep()\n", 309 | " E = np.sum(psi.bond_expectation_value(model.H_bonds))\n", 310 | " print(\"sweep {i:2d}: E = {E:.13f}\".format(i=i + 1, E=E))\n", 311 | "print(\"final bond dimensions: \", psi.get_chi())\n", 312 | "mag_x = np.mean(psi.site_expectation_value(model.sigmax))\n", 313 | "mag_z = np.mean(psi.site_expectation_value(model.sigmaz))\n", 314 | "print(\"magnetization in X = {mag_x:.5f}\".format(mag_x=mag_x))\n", 315 | "print(\"magnetization in Z = {mag_z:.5f}\".format(mag_z=mag_z))" 316 | ] 317 | }, 318 | { 319 | "cell_type": "markdown", 320 | "id": "b0eee6bf", 321 | "metadata": {}, 322 | "source": [ 323 | "### Exercise\n", 324 | "\n", 325 | "Read the code of [d_dmrg.py](https://github.com/tenpy/tenpy_toycodes/blob/main/tenpy_toycodes/d_dmrg.py) and try to understand the general structure of how it works.\n" 326 | ] 327 | }, 328 | { 329 | "cell_type": "markdown", 330 | "id": "dbd12c50", 331 | "metadata": {}, 332 | "source": [ 333 | "### Exercise\n", 334 | "\n", 335 | "Compare running the code cells for DMRG and imaginary time evolution TEBD abovce for various parameters \n", 336 | "of `L`, `J`, `g`, and `bc`. Which one is faster? Do they always agree?\n", 337 | "\n", 338 | "Note: \n", 339 | "The transverse field Ising model has a quantum phase transition at $g/J = 1.$,\n", 340 | "from a ferro-magnetic phase (`g < J `) to a paramagnetic phase (`g > J`). We will map out the full phase diagram below." 341 | ] 342 | }, 343 | { 344 | "cell_type": "code", 345 | "execution_count": null, 346 | "id": "4b3cbccb-a2c1-40bb-97a4-e53faf484552", 347 | "metadata": {}, 348 | "outputs": [], 349 | "source": [] 350 | }, 351 | { 352 | "cell_type": "code", 353 | "execution_count": null, 354 | "id": "2c166517-def8-449c-8466-b765fc5b46da", 355 | "metadata": {}, 356 | "outputs": [], 357 | "source": [] 358 | }, 359 | { 360 | "cell_type": "markdown", 361 | "id": "8717f6a4", 362 | "metadata": {}, 363 | "source": [ 364 | "## Infinite DMRG\n", 365 | "\n", 366 | "The `SimpleDMRG` code also allows to run infinite DMRG, simply by replacing the `bc='finite'` for both the model and the MPS. Look at the implementation of `d_dmrg.py` to see where the differences are.\n", 367 | "\n", 368 | "The `L` parameter now just indices the number of tensors insite the unit cell of the infinite MPS.\n", 369 | "It has to be at least `2`, since we optimize 2 tensors at once in our DMRG code.\n", 370 | "Further, note that we now use the `mean` to calculate densities of observables instead of extensive quantities (like L)." 371 | ] 372 | }, 373 | { 374 | "cell_type": "code", 375 | "execution_count": null, 376 | "id": "76ab2cc8", 377 | "metadata": {}, 378 | "outputs": [], 379 | "source": [ 380 | "model = TFIModel(L=2, J=1., g=0.2, bc='infinite')" 381 | ] 382 | }, 383 | { 384 | "cell_type": "code", 385 | "execution_count": null, 386 | "id": "717abeee", 387 | "metadata": {}, 388 | "outputs": [], 389 | "source": [ 390 | "chi_max = 10\n", 391 | "\n", 392 | "psi = init_FM_MPS(model.L, model.d, model.bc)\n", 393 | "eng = SimpleDMRGEngine(psi, model, chi_max=chi_max, eps=1.e-7)\n", 394 | "for i in range(10):\n", 395 | " E_dmrg = eng.sweep()\n", 396 | " E = np.mean(psi.bond_expectation_value(model.H_bonds))\n", 397 | " print(\"sweep {i:2d}: E/L = {E:.13f}\".format(i=i + 1, E=E))\n", 398 | "print(\"final bond dimensions: \", psi.get_chi())\n", 399 | "mag_x = np.mean(psi.site_expectation_value(model.sigmax))\n", 400 | "mag_z = np.mean(psi.site_expectation_value(model.sigmaz))\n", 401 | "print(\"magnetization in X = {mag_x:.5f}\".format(mag_x=mag_x))\n", 402 | "print(\"magnetization in Z = {mag_z:.5f}\".format(mag_z=mag_z))" 403 | ] 404 | }, 405 | { 406 | "cell_type": "markdown", 407 | "id": "1b260440", 408 | "metadata": {}, 409 | "source": [ 410 | "### Exercise\n", 411 | "\n", 412 | "Extend the following function `run_DMRG` to initialize an MPS compatible with the model, run DMRG on that MPS and return the corresponding state." 413 | ] 414 | }, 415 | { 416 | "cell_type": "code", 417 | "execution_count": null, 418 | "id": "b76969aa", 419 | "metadata": {}, 420 | "outputs": [], 421 | "source": [ 422 | "def run_DMRG(model, chi_max=50):\n", 423 | " print(f\"runnning DMRG for L={model.L:d}, g={model.g:.2f}, bc={model.bc}, chi_max={chi_max:d}\")\n", 424 | " \n", 425 | " raise NotImplementedError(\"TODO: this is an Exercise!\")\n", 426 | " \n", 427 | " return psi" 428 | ] 429 | }, 430 | { 431 | "cell_type": "markdown", 432 | "id": "e9b9796c", 433 | "metadata": {}, 434 | "source": [ 435 | "### Exercise\n", 436 | "\n", 437 | "Use that function to check that the average energy density and expectation values for infinite DMRG are independent of the unit cell length `L`." 438 | ] 439 | }, 440 | { 441 | "cell_type": "code", 442 | "execution_count": null, 443 | "id": "b099f8df-8cdd-47ab-be72-59803ba8a5b7", 444 | "metadata": {}, 445 | "outputs": [], 446 | "source": [] 447 | }, 448 | { 449 | "cell_type": "markdown", 450 | "id": "8f7b39ac", 451 | "metadata": {}, 452 | "source": [ 453 | "### Exercise\n", 454 | "\n", 455 | "Use the `run_DMRG` function above to plot expectation values as a function of `g` for fixed `J=1` with the following code.\n", 456 | "\n", 457 | "\n", 458 | "Modify it to also plot the average expectation values of `sigmaz` and `sigmax` and the \n", 459 | "`sigmaz` correlation function between spin 0 and 20." 460 | ] 461 | }, 462 | { 463 | "cell_type": "code", 464 | "execution_count": null, 465 | "id": "e9a35a7b", 466 | "metadata": { 467 | "scrolled": true 468 | }, 469 | "outputs": [], 470 | "source": [ 471 | "#gs = [0.1, 0.5, 1.0, 1.5, 2.0]\n", 472 | "gs = np.linspace(0., 2., 21)\n", 473 | "entropies = []\n", 474 | "\n", 475 | "for g in gs:\n", 476 | " model = TFIModel(L=2, J=1., g=g, bc='infinite')\n", 477 | " psi = run_DMRG(model, chi_max=50)\n", 478 | " entropies.append(np.max(psi.entanglement_entropy()))\n", 479 | "\n", 480 | " " 481 | ] 482 | }, 483 | { 484 | "cell_type": "code", 485 | "execution_count": null, 486 | "id": "4ce49bc6", 487 | "metadata": {}, 488 | "outputs": [], 489 | "source": [ 490 | "plt.plot(gs, entropies, marker='o', label='max entropy $S$')\n", 491 | "# plot expecation values of sigmax and sigmaz as well\n", 492 | "\n", 493 | "plt.xlabel('$g/J$')\n", 494 | "plt.ylabel('expectation values')\n", 495 | "plt.legend(loc='best')\n" 496 | ] 497 | }, 498 | { 499 | "cell_type": "markdown", 500 | "id": "17bee63e", 501 | "metadata": {}, 502 | "source": [ 503 | "## Defining a new model: the XX chain\n", 504 | "\n", 505 | "For the time evolution, we want to consider the XX chain in a staggered field, given by the Hamiltonian\n", 506 | "\n", 507 | "$$ H = \\sum_{i=0}^{N-2} (\\sigma^x_i \\sigma^x_{i+1} + \\sigma^y_i \\sigma^y_{i+1}) - h_s \\sum_{i=0}^{N-1} (-1)^i \\sigma^z_i \n", 508 | " = 2 \\sum_{i=0}^{N-2} (\\sigma^+_i \\sigma^-_{i+1} + \\sigma^+_i \\sigma^-_{i+1}) - h_s \\sum_{i=0}^{N-1} (-1)^i \\sigma^z_i\n", 509 | "$$\n", 510 | "\n", 511 | "for the usual Pauli matrices $\\sigma^x, \\sigma^y, \\sigma^z$.\n", 512 | "\n", 513 | "A Jordan-Wigner transformation maps the XX Chain to free fermions, which we can diagonalize exactly with a few lines of python codes that are given in [free_fermions_exact.py](https://github.com/tenpy/tenpy_toycodes/blob/main/tenpy_toycodes/free_fermions_exact.py)" 514 | ] 515 | }, 516 | { 517 | "cell_type": "code", 518 | "execution_count": null, 519 | "id": "a23c7236", 520 | "metadata": {}, 521 | "outputs": [], 522 | "source": [ 523 | "from tenpy_toycodes.free_fermions_exact import XX_model_ground_state_energy\n", 524 | "\n", 525 | "print(\"E_exact = \", XX_model_ground_state_energy(L=10, h_staggered=0.))" 526 | ] 527 | }, 528 | { 529 | "cell_type": "markdown", 530 | "id": "2b8919e4", 531 | "metadata": {}, 532 | "source": [ 533 | "### Exercise\n", 534 | "\n", 535 | "The following code implements the model for the XX Chain hamiltonian, but the MPO lacks some terms. Fill them in!\n", 536 | "\n", 537 | "\n", 538 | "Tip: In Python, `(-1)**i` represents $(-1)^i$.\n", 539 | "\n", 540 | "Tip: For the Hamiltonian $$H = \\sum_{i=0}^{N-2} (\\sigma^x_i \\sigma^x_{i+1} + \\sigma^y_i \\sigma^y_{i+1} + \\sigma^z_i \\sigma^z_{i+1}) - h \\sum_{i=0}^{N-1} \\sigma^z_i, $$ a possible MPO matrix W looks like\n", 541 | "$$ W = \\begin{pmatrix}\n", 542 | "1 & \\sigma^x & \\sigma^y & \\sigma^z & -h \\sigma^z \\\\\n", 543 | "0 & 0 & 0 & 0 & \\sigma^x \\\\ \n", 544 | "0 & 0 & 0 & 0 & \\sigma^y \\\\\n", 545 | "0 & 0 & 0 & 0 & \\sigma^z \\\\\n", 546 | "0 & 0 & 0 & 0 & 1 \n", 547 | "\\end{pmatrix} .$$\n", 548 | "Which parts do we need here?\n", 549 | "\n", 550 | "Compare the energies of DMRG and (imaginary) TEBD to check that you got it correct.\n", 551 | "\n", 552 | "**Note** that you have to use the Neel state as initial state to be in the right charge sector:\n", 553 | "the `|up up up ... up >` state already is an eigenstate of this Hamiltonian, so neither DMRG nor TEBD will modify it!" 554 | ] 555 | }, 556 | { 557 | "cell_type": "code", 558 | "execution_count": null, 559 | "id": "33f562a4", 560 | "metadata": {}, 561 | "outputs": [], 562 | "source": [ 563 | "class XXChain:\n", 564 | " \"\"\"Simple class generating the Hamiltonian of the \n", 565 | " The Hamiltonian reads\n", 566 | " .. math ::\n", 567 | " H = - J \\\\sum_{i} \\\\sigma^x_i \\\\sigma^x_{i+1} - g \\\\sum_{i} \\\\sigma^z_i\n", 568 | " \"\"\"\n", 569 | " def __init__(self, L, hs, bc='finite'):\n", 570 | " assert bc in ['finite', 'infinite']\n", 571 | " self.L, self.d, self.bc = L, 2, bc\n", 572 | " self.hs = hs\n", 573 | " self.sigmax = np.array([[0., 1.], [1., 0.]]) # Pauli X\n", 574 | " self.sigmay = np.array([[0., -1j], [1j, 0.]]) # Pauli Y\n", 575 | " self.sigmaz = np.array([[1., 0.], [0., -1.]]) # Pauli Z\n", 576 | " self.id = np.eye(2)\n", 577 | " self.init_H_bonds()\n", 578 | " self.init_H_mpo()\n", 579 | "\n", 580 | " def init_H_bonds(self):\n", 581 | " \"\"\"Initialize `H_bonds` hamiltonian.\"\"\"\n", 582 | " sx, sy, sz, id = self.sigmax, self.sigmay, self.sigmaz, self.id\n", 583 | " d = self.d\n", 584 | " nbonds = self.L - 1 if self.bc == 'finite' else self.L\n", 585 | " H_list = []\n", 586 | " for i in range(nbonds):\n", 587 | " hL = hR = 0.5 * self.hs\n", 588 | " if self.bc == 'finite':\n", 589 | " if i == 0:\n", 590 | " hL = self.hs\n", 591 | " if i + 1 == self.L - 1:\n", 592 | " hR = self.hs\n", 593 | " H_bond = np.kron(sx, sx) + np.kron(sy, sy)\n", 594 | " H_bond = H_bond - hL * (-1)**i * np.kron(sz, id) - hR * (-1)**(i+1) * np.kron(id, sz)\n", 595 | " # H_bond has legs ``i, j, i*, j*``\n", 596 | " H_list.append(np.reshape(H_bond, [d, d, d, d]))\n", 597 | " self.H_bonds = H_list\n", 598 | "\n", 599 | " # (note: not required for TEBD)\n", 600 | " def init_H_mpo(self):\n", 601 | " \"\"\"Initialize `H_mpo` Hamiltonian.\"\"\"\n", 602 | " w_list = []\n", 603 | " for i in range(self.L):\n", 604 | " w = np.zeros((4, 4, self.d, self.d), dtype=complex)\n", 605 | " w[0, 0] = w[3, 3] = self.id\n", 606 | " \n", 607 | " raise NotImplementedError(\"add further entries here\")\n", 608 | " \n", 609 | " w_list.append(w)\n", 610 | " self.H_mpo = w_list\n", 611 | " \n", 612 | "model = XXChain(9, 4., bc='finite')" 613 | ] 614 | }, 615 | { 616 | "cell_type": "code", 617 | "execution_count": null, 618 | "id": "5021c8b7", 619 | "metadata": {}, 620 | "outputs": [], 621 | "source": [ 622 | "print(\"E_exact = \", XX_model_ground_state_energy(model.L, model.hs))" 623 | ] 624 | }, 625 | { 626 | "cell_type": "code", 627 | "execution_count": null, 628 | "id": "7dd03222", 629 | "metadata": {}, 630 | "outputs": [], 631 | "source": [ 632 | "chi_max = 100\n", 633 | "\n", 634 | "psi = init_Neel_MPS(model.L, model.d, model.bc) # important: Neel\n", 635 | "for dt in [0.1, 0.01, 0.001, 1.e-4, 1.e-5]:\n", 636 | " U_bonds = calc_U_bonds(model.H_bonds, dt)\n", 637 | " run_TEBD(psi, U_bonds, N_steps=100, chi_max=chi_max, eps=1.e-10)\n", 638 | " E = np.sum(psi.bond_expectation_value(model.H_bonds))\n", 639 | " print(\"dt = {dt:.5f}: E = {E:.13f}\".format(dt=dt, E=E))" 640 | ] 641 | }, 642 | { 643 | "cell_type": "code", 644 | "execution_count": null, 645 | "id": "a4f5fc2e", 646 | "metadata": {}, 647 | "outputs": [], 648 | "source": [ 649 | "# DMRG again\n", 650 | "chi_max = 100\n", 651 | "psi = init_Neel_MPS(model.L, model.d, model.bc) # important: Neel!\n", 652 | "eng = SimpleDMRGEngine(psi, model, chi_max=chi_max, eps=1.e-10)\n", 653 | "for i in range(10):\n", 654 | " eng.sweep()\n", 655 | " E = np.sum(psi.bond_expectation_value(model.H_bonds))\n", 656 | " #print(psi.get_chi())\n", 657 | " print(\"sweep {i:2d}: E = {E:.13f}\".format(i=i + 1, E=E))" 658 | ] 659 | }, 660 | { 661 | "cell_type": "code", 662 | "execution_count": null, 663 | "id": "42fe4054", 664 | "metadata": {}, 665 | "outputs": [], 666 | "source": [] 667 | }, 668 | { 669 | "cell_type": "markdown", 670 | "id": "4969359f", 671 | "metadata": {}, 672 | "source": [ 673 | "## Time evolution of the Neel state under the XX chain\n", 674 | "\n", 675 | "We will now consider the Neel state as an initial state for (real-)time evolution, i.e., calculate\n", 676 | "$$ \\vert \\psi(t)\\rangle =e^{-i H t} \\vert\\uparrow \\downarrow \\uparrow \\downarrow \\dots\\rangle$$\n", 677 | "under the Hamiltonian of the XX chain (with staggered field).\n", 678 | "\n", 679 | "Again, we can use the mapping to free fermions to calculate some quantities exactly.\n", 680 | "We will focus on the half chain entanglement entropy.\n" 681 | ] 682 | }, 683 | { 684 | "cell_type": "code", 685 | "execution_count": null, 686 | "id": "9d9fc268", 687 | "metadata": {}, 688 | "outputs": [], 689 | "source": [ 690 | "from tenpy_toycodes.free_fermions_exact import XX_model_time_evolved_entropies\n", 691 | "\n", 692 | "dt = 0.1\n", 693 | "tmax = 4.\n", 694 | "L = 50\n", 695 | "hs = 0.\n", 696 | "model = XXChain(L, hs)\n", 697 | "\n", 698 | "times_exact = np.arange(0., tmax, dt)\n", 699 | "S_exact = XX_model_time_evolved_entropies(L=L, h_staggered=hs, time_list=times_exact)" 700 | ] 701 | }, 702 | { 703 | "cell_type": "markdown", 704 | "id": "777211cf", 705 | "metadata": {}, 706 | "source": [ 707 | "Running TEBD works like this:" 708 | ] 709 | }, 710 | { 711 | "cell_type": "code", 712 | "execution_count": null, 713 | "id": "12f349e4", 714 | "metadata": { 715 | "scrolled": true 716 | }, 717 | "outputs": [], 718 | "source": [ 719 | "chi_max = 30\n", 720 | "\n", 721 | "psi = init_Neel_MPS(L, 2, bc='finite')\n", 722 | "U_bonds = calc_U_bonds(model.H_bonds, 1.j * dt) # here, imaginary dt = real time evolution\n", 723 | "t = 0.\n", 724 | "times_tebd = []\n", 725 | "S_tebd = []\n", 726 | "while t < tmax: \n", 727 | " times_tebd.append(t)\n", 728 | " S_tebd.append(psi.entanglement_entropy()[(L-1)//2])\n", 729 | " print(f\"t={t:.2f}\")\n", 730 | " t += dt\n", 731 | " run_TEBD(psi, U_bonds, N_steps=1, chi_max=chi_max, eps=1.e-7)\n" 732 | ] 733 | }, 734 | { 735 | "cell_type": "markdown", 736 | "id": "8b3e02d9-6205-4667-bd44-44c0cf9f6f84", 737 | "metadata": {}, 738 | "source": [ 739 | "For reference, one can also run TDVP with the [e_tdvp.py](https://github.com/tenpy/tenpy_toycodes/blob/main/tenpy_toycodes/e_tdvp.py) as follows.\n", 740 | "If you know how TDVP works, try to take a look and spot the structure in the code.\n", 741 | "Otherwise, you can also consider it a black box for now.\n", 742 | "\n", 743 | "The toycode implements the TDVP time evolution for finite MPS with the `SimpleTDVPEngine`.\n", 744 | "It implements both the true TDVP with one-site updates in `sweep_one_site`, and `sweep_two_site` that allows to grow the bond dimension." 745 | ] 746 | }, 747 | { 748 | "cell_type": "code", 749 | "execution_count": null, 750 | "id": "9c6f5f2e", 751 | "metadata": { 752 | "scrolled": true 753 | }, 754 | "outputs": [], 755 | "source": [ 756 | "chi_max = 30\n", 757 | "t_switch = 1.\n", 758 | "\n", 759 | "psi = init_Neel_MPS(L, 2, bc='finite')\n", 760 | "eng = SimpleTDVPEngine(psi, model, chi_max=chi_max, eps=1.e-7)\n", 761 | "\n", 762 | "t = 0.\n", 763 | "times_tdvp = []\n", 764 | "S_tdvp = []\n", 765 | "while t < tmax: \n", 766 | " times_tdvp.append(t)\n", 767 | " S_tdvp.append(psi.entanglement_entropy()[(L-1)//2])\n", 768 | " print(f\"t={t:.2f}, chi_max = {max(psi.get_chi()):d}\")\n", 769 | " t += dt\n", 770 | " if t < t_switch:\n", 771 | " eng.sweep_two_site(dt)\n", 772 | " else:\n", 773 | " eng.sweep_one_site(dt)" 774 | ] 775 | }, 776 | { 777 | "cell_type": "code", 778 | "execution_count": null, 779 | "id": "8139c6a8", 780 | "metadata": {}, 781 | "outputs": [], 782 | "source": [] 783 | }, 784 | { 785 | "cell_type": "code", 786 | "execution_count": null, 787 | "id": "667f930e", 788 | "metadata": {}, 789 | "outputs": [], 790 | "source": [ 791 | "plt.plot(times_exact, S_exact, label=\"exact\")\n", 792 | "plt.plot(times_tebd, S_tebd, label=\"TEBD\")\n", 793 | "plt.plot(times_tdvp, S_tdvp, label=\"TDVP\")\n", 794 | "\n", 795 | "plt.xlabel('time $t$')\n", 796 | "plt.ylabel('half-chain entropy $S$')\n", 797 | "plt.legend(loc='best')" 798 | ] 799 | }, 800 | { 801 | "cell_type": "markdown", 802 | "id": "9553b647", 803 | "metadata": {}, 804 | "source": [ 805 | "### Exercise\n", 806 | "\n", 807 | "Add curves for smaller (or larger) chi to the plot above. (It might take a little while to run for large chi...).\n", 808 | "\n", 809 | "Try switching earlier or later from two-site to one-site TDVP.\n", 810 | "\n", 811 | "What happens if you switch on the staggered field?\n", 812 | "\n", 813 | "\n", 814 | "**NOTE**: You can avoid having to rerun the time evolution if you make copies of the results `S_tdvp` and similar lists.\n" 815 | ] 816 | }, 817 | { 818 | "cell_type": "code", 819 | "execution_count": null, 820 | "id": "25e5c2a1-60aa-473c-a5d0-d9ab7e7ac8e4", 821 | "metadata": {}, 822 | "outputs": [], 823 | "source": [] 824 | }, 825 | { 826 | "cell_type": "code", 827 | "execution_count": null, 828 | "id": "5a7682dc-483f-4ada-8bcf-b4f9443c256e", 829 | "metadata": {}, 830 | "outputs": [], 831 | "source": [] 832 | }, 833 | { 834 | "cell_type": "code", 835 | "execution_count": null, 836 | "id": "d16df3aa", 837 | "metadata": {}, 838 | "outputs": [], 839 | "source": [] 840 | }, 841 | { 842 | "cell_type": "markdown", 843 | "id": "1e99ab49", 844 | "metadata": {}, 845 | "source": [ 846 | "### Advanced exercises - if you're an expert and have time left ;-)\n", 847 | "\n", 848 | "\n", 849 | "- Obtain the ground state of the transverse field ising model at the critical point with DMRG.\n", 850 | " Try to plot the corrlation function as a function of `j-i`.\n", 851 | " What form does it have? Is an MPS a good ansatz for that?\n", 852 | "- Calling `SimpleMPS.correlation_function` in a loop over `j` for fixed `i` is inefficient for large `j-i`. \n", 853 | " Try to rewrite the correlation function to obtain the values for all `j>i` up to some cutoff in a single loop.\n", 854 | "- Look at the light-cone after a local quench of the XX chain, similarly as is done in `e_tdvp.example_TDVP_tf_ising_lightcone`. \n", 855 | " How does the speed of the light cone change with the staggered field?" 856 | ] 857 | }, 858 | { 859 | "cell_type": "code", 860 | "execution_count": null, 861 | "id": "1dfbc061", 862 | "metadata": {}, 863 | "outputs": [], 864 | "source": [] 865 | }, 866 | { 867 | "cell_type": "code", 868 | "execution_count": null, 869 | "id": "b9d25343", 870 | "metadata": {}, 871 | "outputs": [], 872 | "source": [] 873 | }, 874 | { 875 | "cell_type": "code", 876 | "execution_count": null, 877 | "id": "da649492-fe07-437f-931a-7843a3e4a6a4", 878 | "metadata": {}, 879 | "outputs": [], 880 | "source": [] 881 | }, 882 | { 883 | "cell_type": "code", 884 | "execution_count": null, 885 | "id": "97801804-24e0-4261-912f-699fc8b93228", 886 | "metadata": {}, 887 | "outputs": [], 888 | "source": [] 889 | } 890 | ], 891 | "metadata": { 892 | "kernelspec": { 893 | "display_name": "Python 3 (ipykernel)", 894 | "language": "python", 895 | "name": "python3" 896 | }, 897 | "language_info": { 898 | "codemirror_mode": { 899 | "name": "ipython", 900 | "version": 3 901 | }, 902 | "file_extension": ".py", 903 | "mimetype": "text/x-python", 904 | "name": "python", 905 | "nbconvert_exporter": "python", 906 | "pygments_lexer": "ipython3", 907 | "version": "3.10.6" 908 | } 909 | }, 910 | "nbformat": 4, 911 | "nbformat_minor": 5 912 | } 913 | -------------------------------------------------------------------------------- /exercise_1_basics.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "2fe29049", 6 | "metadata": {}, 7 | "source": [ 8 | "# Tensor basics" 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "id": "3c8f3846-988b-4d6b-8f4e-078c5269df10", 14 | "metadata": {}, 15 | "source": [ 16 | "Here, we cover some very basic concepts of tensor networks:\n", 17 | "\n", 18 | "- how to represent tensors numerically, and\n", 19 | "- what the basic operations are that we need for tensors\n", 20 | "\n", 21 | "For these (toy code) exercises will use the python [numpy](https://numpy.org/doc/stable/user/quickstart.html) librabry for simplicity. For production MPS code, you should eventually switch to a more generic library, e.g., [TeNPy](https://github.com/tenpy/tenpy), which provide a more powerfull class for representing tensors - this allows to make use of block-sparseness implied by symmetries and helps with index bookkeeping." 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": null, 27 | "id": "82932acc-1850-4cda-a34b-2309bde3371d", 28 | "metadata": {}, 29 | "outputs": [], 30 | "source": [ 31 | "import numpy as np" 32 | ] 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "id": "35a02910-a23b-496f-b1af-2dd566a3a20a", 37 | "metadata": {}, 38 | "source": [ 39 | "For later use, let us also import matplotlib and set some cosmetic display options." 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": null, 45 | "id": "b7d6aedd", 46 | "metadata": {}, 47 | "outputs": [], 48 | "source": [ 49 | "import matplotlib.pyplot as plt\n", 50 | "\n", 51 | "np.set_printoptions(precision=5, suppress=True, linewidth=100, threshold=50)\n", 52 | "plt.rcParams['figure.dpi'] = 150" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "id": "0ec28c62-4028-42ee-80ba-6c4370111024", 58 | "metadata": {}, 59 | "source": [ 60 | "## Initializing tensors in numpy\n", 61 | "\n", 62 | "From a mathematical point of view, tensors are multi-linear function between vector spaces.\n", 63 | "From a numerical point of view, however, tensors are just collections of numbers arranged in a multi-dimensional grid, with element-wise addition and tensordot contractions over one or multiple legs.\n", 64 | "\n", 65 | "In this set of toycodes, we represent tensors directly as numpy arrays. \n", 66 | "The following examples demonstrate different ways to initialize tensors, here for the usual `2x2` Pauli matrices:" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": null, 72 | "id": "778f138c-f9b3-4889-baca-e1000d4bf05e", 73 | "metadata": {}, 74 | "outputs": [], 75 | "source": [ 76 | "# np.eye = identity matrix of given dimension 2\n", 77 | "Id = np.eye(2) \n", 78 | "Id" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": null, 84 | "id": "88b69153-267d-47eb-bca8-f7f0e97ad0bc", 85 | "metadata": {}, 86 | "outputs": [], 87 | "source": [ 88 | "# specify each element \n", 89 | "X = np.array([[0, 1.], [1., 0.]]) \n", 90 | "X" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": null, 96 | "id": "3714f0a7-2b92-46df-bc61-f7ed5a91cd4c", 97 | "metadata": {}, 98 | "outputs": [], 99 | "source": [ 100 | "# initialize empty matrix of dimensions 2x2\n", 101 | "Y = np.zeros([2, 2], dtype=np.complex_) \n", 102 | "# and explicitly set non-zero entries\n", 103 | "Y[0, 1] = -1.j # j = imaginary unit\n", 104 | "Y[1, 0] = 1.j\n", 105 | "Y" 106 | ] 107 | }, 108 | { 109 | "cell_type": "code", 110 | "execution_count": null, 111 | "id": "3dd23ebe-5ed1-4b3e-b0e7-cfa81bd7caab", 112 | "metadata": {}, 113 | "outputs": [], 114 | "source": [ 115 | "# only specify the diagonal of a matrix\n", 116 | "Z = np.diag([1., -1.]) \n", 117 | "Z" 118 | ] 119 | }, 120 | { 121 | "cell_type": "markdown", 122 | "id": "2cc74936-5116-4ec4-824d-f6d2f4e877cc", 123 | "metadata": {}, 124 | "source": [ 125 | "They are all simple `2x2` matrices, as we can see by checking the `shape`. Only the `Y` matrix has complex data type (`dtype`):" 126 | ] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "execution_count": null, 131 | "id": "630abe6c-fc4c-48c4-8382-2ddb988d4da8", 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [ 135 | "for M in [Id, X, Y, Z]:\n", 136 | " print(M.shape, M.dtype)" 137 | ] 138 | }, 139 | { 140 | "cell_type": "markdown", 141 | "id": "1a1d2ea4-17d8-4de8-8133-175716383bee", 142 | "metadata": {}, 143 | "source": [ 144 | "If we consider the Pauli matrices as spin-1/2 operators, we already implicitly assumed the basis to be $\\lvert\\uparrow\\rangle,\\lvert\\downarrow\\rangle$." 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": null, 150 | "id": "3b336217-4c28-41ce-b351-e15906f68f0c", 151 | "metadata": {}, 152 | "outputs": [], 153 | "source": [ 154 | "up = np.array([1., 0])\n", 155 | "up" 156 | ] 157 | }, 158 | { 159 | "cell_type": "code", 160 | "execution_count": null, 161 | "id": "a2b446cc-8f55-42e2-8bc0-c4712eb6a4f7", 162 | "metadata": {}, 163 | "outputs": [], 164 | "source": [ 165 | "down = np.array([0., 1.])\n", 166 | "down" 167 | ] 168 | }, 169 | { 170 | "cell_type": "markdown", 171 | "id": "ce9d73e7-4c8e-4894-9974-dfb9add88226", 172 | "metadata": {}, 173 | "source": [ 174 | "## Element-wise operations\n", 175 | "Element-wise addition of tensors and multiplication/division with scalars works as you might expect." 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": null, 181 | "id": "e537ae4e-4fd0-4117-b4ab-96568668bc76", 182 | "metadata": {}, 183 | "outputs": [], 184 | "source": [ 185 | "Z + 5 * X" 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": null, 191 | "id": "b0289b7d-2c78-4882-ba49-98f7f6cd8dbe", 192 | "metadata": {}, 193 | "outputs": [], 194 | "source": [ 195 | "plus_x = (up + down) / np.sqrt(2) # sqrt = square root\n", 196 | "plus_x" 197 | ] 198 | }, 199 | { 200 | "cell_type": "markdown", 201 | "id": "b2697293-28ba-43e7-abdb-6e2be7cb24d2", 202 | "metadata": {}, 203 | "source": [ 204 | "Complex conjugation can be acchieved with `np.conj()` (or the `conj()` method of numpy arrays)." 205 | ] 206 | }, 207 | { 208 | "cell_type": "code", 209 | "execution_count": null, 210 | "id": "1b6d2380-fea2-4425-b9f1-4eb2d7a3a5dd", 211 | "metadata": {}, 212 | "outputs": [], 213 | "source": [ 214 | "Y, np.conj(Y)" 215 | ] 216 | }, 217 | { 218 | "cell_type": "markdown", 219 | "id": "6f9bbba5-dd3c-4f71-bf3a-1665e2cccc3f", 220 | "metadata": {}, 221 | "source": [ 222 | "### Exercise: init `minus_x`\n", 223 | "\n", 224 | "Initialize the state `minus_x` $= \\frac{1}{\\sqrt{2}} \\big( \\lvert\\uparrow \\rangle - \\lvert\\downarrow\\rangle \\big)$" 225 | ] 226 | }, 227 | { 228 | "cell_type": "code", 229 | "execution_count": null, 230 | "id": "7940af87-faa4-48fb-bfd7-e676b151bd64", 231 | "metadata": {}, 232 | "outputs": [], 233 | "source": [] 234 | }, 235 | { 236 | "cell_type": "code", 237 | "execution_count": null, 238 | "id": "3ab56d95-a143-42f7-9c59-15362a033d28", 239 | "metadata": {}, 240 | "outputs": [], 241 | "source": [] 242 | }, 243 | { 244 | "cell_type": "markdown", 245 | "id": "887424c9-b3d5-4ecd-9245-cfcdc396fbcc", 246 | "metadata": {}, 247 | "source": [ 248 | "## Random vector\n", 249 | "We can also define a small utility function to define a random (complex) vector pointing uniformly in any direction:" 250 | ] 251 | }, 252 | { 253 | "cell_type": "code", 254 | "execution_count": null, 255 | "id": "49e0f906-3578-48ae-a5b7-a531ececc671", 256 | "metadata": {}, 257 | "outputs": [], 258 | "source": [ 259 | "def random_vector(size, rng=np.random.default_rng(seed=1234)):\n", 260 | " \"\"\"Return a normalized random vector of given shape pointing uniformly in any direction.\"\"\"\n", 261 | " vector = rng.normal(size=size) + 1.j * rng.normal(size=size)\n", 262 | " return vector / np.linalg.norm(vector)" 263 | ] 264 | }, 265 | { 266 | "cell_type": "code", 267 | "execution_count": null, 268 | "id": "01f4dabc-1057-499d-9865-da04ec5824fe", 269 | "metadata": {}, 270 | "outputs": [], 271 | "source": [ 272 | "random_vector(3)" 273 | ] 274 | }, 275 | { 276 | "cell_type": "markdown", 277 | "id": "4edce430-be9d-4c6b-8d5f-79946487b0b5", 278 | "metadata": {}, 279 | "source": [ 280 | "## Leg label convention\n", 281 | "\n", 282 | "Note that the order of tensor legs is important; and we need to keep track of it. \n", 283 | "Libraries like TeNPy can help with that bookkeeping, but here we keep it simple, and just write leg labels as comments in the code.\n", 284 | "\n", 285 | "- For ket (vector) indices, we just use short identifiers, e.g. `i`, `j`, `i1`, `i2`, `vL`.\n", 286 | "- For bra (dual) indices, we use an additional `*`, so calling `conj()` on a tensor toggles stars on all legs, and (square) operators should have combinations of non-star and star labels.\n", 287 | "- When contracting (examples further down), we indicate labels that are getting contacted over with square brackets.\n", 288 | "- When grouping legs (examples further down), we combine labels in round brackets separated by dots, e.g. `(i.j)`." 289 | ] 290 | }, 291 | { 292 | "cell_type": "code", 293 | "execution_count": null, 294 | "id": "3d961ef6-06e0-472b-9f64-040727e43039", 295 | "metadata": {}, 296 | "outputs": [], 297 | "source": [ 298 | "plus_x # leg i\n", 299 | "X # legs i i*\n", 300 | "np.conj(plus_x) # leg i*" 301 | ] 302 | }, 303 | { 304 | "cell_type": "markdown", 305 | "id": "12c3ddef-da67-4a92-ade2-401cdbe32781", 306 | "metadata": {}, 307 | "source": [ 308 | "## More spins and legs, transpose and reshape to combine and split legs\n", 309 | "\n", 310 | "When we have more than one spin, the Hilbert space is a an outer product of the local Hilbert spaces. In tensor network language, this corresponds to the fact that we can have one leg per spin.\n", 311 | "\n", 312 | "For example, the singlet state $\\frac{1}{\\sqrt{2}}\\big(\\lvert \\uparrow \\rangle \\otimes \\lvert\\downarrow \\rangle - \\lvert \\downarrow \\rangle \\otimes \\lvert\\uparrow \\rangle \\big)$ on two spins could be represented by a two-leg tensor:" 313 | ] 314 | }, 315 | { 316 | "cell_type": "code", 317 | "execution_count": null, 318 | "id": "71d6e1b3-4d5e-4da8-a22e-dc3d57cadf43", 319 | "metadata": {}, 320 | "outputs": [], 321 | "source": [ 322 | "singlet = np.zeros([2, 2]) # legs i, j\n", 323 | "singlet[0, 1] = 1./np.sqrt(2)\n", 324 | "singlet[1, 0] = -1./np.sqrt(2)\n", 325 | "singlet" 326 | ] 327 | }, 328 | { 329 | "cell_type": "markdown", 330 | "id": "38943568-c256-4fce-b05f-3487d684ab38", 331 | "metadata": {}, 332 | "source": [ 333 | "Alternatively, we can enumerate all possible combinations basis states (in lexiographical order),\n", 334 | "in the case of two spins $\\lvert\\uparrow\\uparrow\\rangle, \\lvert\\uparrow\\downarrow\\rangle, \\lvert\\downarrow\\uparrow\\rangle, \\lvert\\downarrow\\downarrow\\rangle$, and use a single leg.\n", 335 | "\n", 336 | "In other words, we can **combine** (or \"**group**\") the two legs to a single one.\n", 337 | "For numpy arrays, this corresponds to a `reshape`, taking dimensions `d1` and `d2` of the individual legs to `d1 * d2`. It is merely a re-arranging of the tensors in a different grid, the entries are still the same:" 338 | ] 339 | }, 340 | { 341 | "cell_type": "code", 342 | "execution_count": null, 343 | "id": "66602ef4-bb7b-4110-a109-f9c4b611284f", 344 | "metadata": {}, 345 | "outputs": [], 346 | "source": [ 347 | "singlet_group = np.reshape(singlet, [2*2]) # legs i j -> (i.j)\n", 348 | "singlet_group" 349 | ] 350 | }, 351 | { 352 | "cell_type": "markdown", 353 | "id": "1cd3ee05-027d-47bb-9339-687a4c1f7367", 354 | "metadata": {}, 355 | "source": [ 356 | "If we remember the order and dimensions of the combined legs, we can split it again by another reshape:" 357 | ] 358 | }, 359 | { 360 | "cell_type": "code", 361 | "execution_count": null, 362 | "id": "2f96f59b-d094-4fad-b761-e706b6d540d3", 363 | "metadata": {}, 364 | "outputs": [], 365 | "source": [ 366 | "singlet_split = np.reshape(singlet_group, [2, 2]) # legs (i.j) -> i j\n", 367 | "singlet_split" 368 | ] 369 | }, 370 | { 371 | "cell_type": "markdown", 372 | "id": "955235e2-42ba-4d2a-b52d-7aa5aa2cd7a3", 373 | "metadata": {}, 374 | "source": [ 375 | "All of this generalizes to multiple legs, as long as we `transpose` legs to be combined next to each other.\n", 376 | "\n", 377 | "For example, if we initialize a random vector $\\theta_{a,b,c,d}$ and want to group legs as `a b c d -> (a.c) (b.d)`, we need to transpose first:" 378 | ] 379 | }, 380 | { 381 | "cell_type": "code", 382 | "execution_count": null, 383 | "id": "5524ef3d-bd93-4b8e-a0c8-135108329411", 384 | "metadata": {}, 385 | "outputs": [], 386 | "source": [ 387 | "theta = random_vector([2, 3, 4, 5]) # legs a b c d\n", 388 | "print(theta.shape)" 389 | ] 390 | }, 391 | { 392 | "cell_type": "code", 393 | "execution_count": null, 394 | "id": "65f18475-cd12-46a2-b295-0fe26e3f30b9", 395 | "metadata": {}, 396 | "outputs": [], 397 | "source": [ 398 | "theta_group = np.reshape(np.transpose(theta, [0, 2, 1, 3]), # a b c d -> a c b d\n", 399 | " [2*4, 3*5]) # a c b d -> (a.c) (b.d)\n", 400 | "theta_group.shape" 401 | ] 402 | }, 403 | { 404 | "cell_type": "code", 405 | "execution_count": null, 406 | "id": "85009e8f-c5d6-4460-b386-9ccc878c2054", 407 | "metadata": {}, 408 | "outputs": [], 409 | "source": [ 410 | "theta_split = np.reshape(theta_group, [2, 4, 3, 5]) # (a.c) (b.d) -> a c b d" 411 | ] 412 | }, 413 | { 414 | "cell_type": "markdown", 415 | "id": "c2748951-1359-4f72-a67d-b7cb52f478a4", 416 | "metadata": {}, 417 | "source": [ 418 | "To get back to the original form, we need to transpose again:" 419 | ] 420 | }, 421 | { 422 | "cell_type": "code", 423 | "execution_count": null, 424 | "id": "f4afaf91-aa36-4d31-b6a1-2c07d85a3504", 425 | "metadata": {}, 426 | "outputs": [], 427 | "source": [ 428 | "np.linalg.norm(theta_split.transpose([0, 2, 1, 3]) # a c b d -> a b c d\n", 429 | " -theta)" 430 | ] 431 | }, 432 | { 433 | "cell_type": "markdown", 434 | "id": "fabf5b0a-e6ff-4ed6-9fcc-7b6772230f77", 435 | "metadata": {}, 436 | "source": [ 437 | "### Exercise: reshape\n", 438 | "\n", 439 | "Initialize a random vector `psi` with 6 legs of length 2, i.e. shape `(2, 2, 2, 2, 2, 2)`. \n", 440 | "\n", 441 | "Afterwards, reshape it into a matrix with each the first and last 3 legs combined.\n", 442 | "\n", 443 | "*Tip*: In Python, you can use `[2]*5` to get a list `[2, ..., 2]` with 5 entries, \n", 444 | "and `2**5` $= 2^5$." 445 | ] 446 | }, 447 | { 448 | "cell_type": "code", 449 | "execution_count": null, 450 | "id": "127e0ab6-71b2-4377-9afa-7c8b61ff75ad", 451 | "metadata": {}, 452 | "outputs": [], 453 | "source": [] 454 | }, 455 | { 456 | "cell_type": "code", 457 | "execution_count": null, 458 | "id": "c8df83fa-3310-40fc-9144-db3bc9ea3fba", 459 | "metadata": {}, 460 | "outputs": [], 461 | "source": [] 462 | }, 463 | { 464 | "cell_type": "markdown", 465 | "id": "79bd7296-654f-4188-9f0a-0c6691f353d7", 466 | "metadata": {}, 467 | "source": [ 468 | "## Contractions with tensordot\n", 469 | "\n", 470 | "The `np.tensordot` function performs contractions between two tensors along legs specified in the `axes` argument.\n", 471 | " \n", 472 | "For example for the usual matrix-vector product $\\hat{X} \\lvert \\uparrow\\rangle$, we need to contract the second leg (index 1, since we start counting at 0) of `X` with the first (and only, index 0) leg of the `up` vector:" 473 | ] 474 | }, 475 | { 476 | "cell_type": "code", 477 | "execution_count": null, 478 | "id": "b1085e85-9b51-4984-b8e6-3e518c7790f6", 479 | "metadata": {}, 480 | "outputs": [], 481 | "source": [ 482 | "np.tensordot(X, up, axes=(1, 0)) # i [i*], [i] -> i" 483 | ] 484 | }, 485 | { 486 | "cell_type": "markdown", 487 | "id": "17d06f51-a069-42e4-aff4-3afe21199865", 488 | "metadata": {}, 489 | "source": [ 490 | "Matrix multiplication looks similar, except that the second and the resulting tensor have one more leg:" 491 | ] 492 | }, 493 | { 494 | "cell_type": "code", 495 | "execution_count": null, 496 | "id": "1a467af4-0950-4628-8b18-a5aeda1f285a", 497 | "metadata": {}, 498 | "outputs": [], 499 | "source": [ 500 | "np.tensordot(X, Y, axes=(1, 0)) # i [i*], [i] i* -> i i*" 501 | ] 502 | }, 503 | { 504 | "cell_type": "markdown", 505 | "id": "df541397-b01a-4eb8-be18-fd45a1d8d144", 506 | "metadata": {}, 507 | "source": [ 508 | "If we contract over all legs, we effectively perform an inner product and obtain a scalar:" 509 | ] 510 | }, 511 | { 512 | "cell_type": "code", 513 | "execution_count": null, 514 | "id": "5349fd63-eff5-42ee-a760-a871f62d77a8", 515 | "metadata": {}, 516 | "outputs": [], 517 | "source": [ 518 | "np.tensordot(np.conj(plus_x), up, axes=(0, 0)) # [i*], [i] -> scalar" 519 | ] 520 | }, 521 | { 522 | "cell_type": "markdown", 523 | "id": "7d663f67-bef0-448e-a1ac-2cfefcba2167", 524 | "metadata": {}, 525 | "source": [ 526 | "If we set `axes=0`, we obtain an outer product, e.g. if we want to get the operator `ZZ` $= Z_1 \\otimes Z_2$ acting on two spins:" 527 | ] 528 | }, 529 | { 530 | "cell_type": "code", 531 | "execution_count": null, 532 | "id": "be032d25-c88b-4268-8673-592c4b304624", 533 | "metadata": {}, 534 | "outputs": [], 535 | "source": [ 536 | "ZZ = np.tensordot(Z, Z, axes=0) # i i*, j j* -> i i* j j*" 537 | ] 538 | }, 539 | { 540 | "cell_type": "markdown", 541 | "id": "0717d10b-fb92-43ff-9155-9151354b455d", 542 | "metadata": {}, 543 | "source": [ 544 | "For printing the result, let us group the in- and outgoing legs:" 545 | ] 546 | }, 547 | { 548 | "cell_type": "code", 549 | "execution_count": null, 550 | "id": "64eacdba-1ab1-4bf7-8e7e-6c695bc9d2ce", 551 | "metadata": {}, 552 | "outputs": [], 553 | "source": [ 554 | "ZZ_g = np.transpose(ZZ, [0, 2, 1, 3]) # i i* j j* -> i j i* j*\n", 555 | "ZZ_g = np.reshape(ZZ_g, [2*2, 2*2]) # i j i* j* -> (i.j) (i*.j*)\n", 556 | "ZZ_g" 557 | ] 558 | }, 559 | { 560 | "cell_type": "markdown", 561 | "id": "f0c867a0-957c-499c-9767-b0cbd21badbe", 562 | "metadata": {}, 563 | "source": [ 564 | "Finally, to contract multiple legs, we can pass two lists to `axes`:" 565 | ] 566 | }, 567 | { 568 | "cell_type": "code", 569 | "execution_count": null, 570 | "id": "c5c4ded0-74b1-4bcd-a678-bcd593072d8e", 571 | "metadata": {}, 572 | "outputs": [], 573 | "source": [ 574 | "ZZ # i i* j j*\n", 575 | "Theta = random_vector([3, 2, 2, 4]) # a i j b\n", 576 | "# contract i* of ZZ with \n", 577 | "ZZ_Theta = np.tensordot(ZZ, Theta, axes=([1, 3], [1, 2])) # i [i*] j [j*], a [i] [j] b -> i j a b\n", 578 | "ZZ_Theta.shape" 579 | ] 580 | }, 581 | { 582 | "cell_type": "markdown", 583 | "id": "153f8352-8ad1-4df6-99a9-a3001d243483", 584 | "metadata": {}, 585 | "source": [ 586 | "### Exercise: contractions\n", 587 | "\n", 588 | "- Contract the following expectation values using `tensordot` and check that you get the correct results:\n", 589 | " - ` =?= 1.`\n", 590 | " - ` =?= 1.`\n", 591 | " - ` =?= 0.`\n", 592 | " " 593 | ] 594 | }, 595 | { 596 | "cell_type": "code", 597 | "execution_count": null, 598 | "id": "afa30bf1-dce9-4abe-9bb7-117a79988bf2", 599 | "metadata": {}, 600 | "outputs": [], 601 | "source": [] 602 | }, 603 | { 604 | "cell_type": "code", 605 | "execution_count": null, 606 | "id": "2e933507-f357-4f44-aaf9-63633278bbf7", 607 | "metadata": {}, 608 | "outputs": [], 609 | "source": [] 610 | }, 611 | { 612 | "cell_type": "code", 613 | "execution_count": null, 614 | "id": "35340495-7b0d-4981-a3a6-e9f3f6c207c5", 615 | "metadata": {}, 616 | "outputs": [], 617 | "source": [] 618 | }, 619 | { 620 | "cell_type": "markdown", 621 | "id": "ab274679-bdb4-4643-8eb7-fbe43e0087eb", 622 | "metadata": {}, 623 | "source": [ 624 | "\n", 625 | "- Define the operator `h` $= 4 \\vec{S_1} \\cdot \\vec{S_2} = X_1 X_2 + Y_1 Y_2 + Z_1 Z_2$ with 4 legs.\n" 626 | ] 627 | }, 628 | { 629 | "cell_type": "code", 630 | "execution_count": null, 631 | "id": "3d450e8a-d2c0-42ed-90d6-e3ff8c8ab4ca", 632 | "metadata": {}, 633 | "outputs": [], 634 | "source": [] 635 | }, 636 | { 637 | "cell_type": "markdown", 638 | "id": "45090e1d-84df-4f7e-b264-a4ad9894b049", 639 | "metadata": {}, 640 | "source": [ 641 | "- Using `tensordot`, contract and check ` =?= -3`" 642 | ] 643 | }, 644 | { 645 | "cell_type": "code", 646 | "execution_count": null, 647 | "id": "a53b92c3-6754-4805-9b05-d25af90ff4d8", 648 | "metadata": {}, 649 | "outputs": [], 650 | "source": [] 651 | }, 652 | { 653 | "cell_type": "markdown", 654 | "id": "4ba0a7f8-20d7-4432-aaf8-21a394ae87f8", 655 | "metadata": {}, 656 | "source": [ 657 | "## Schmidt decomposition with SVD" 658 | ] 659 | }, 660 | { 661 | "cell_type": "markdown", 662 | "id": "16143401-d426-4c38-88b7-96219a4b8059", 663 | "metadata": {}, 664 | "source": [ 665 | "The final tool we need are matrix operations, e.g., diagonalization, SVD or QR decomposition.\n", 666 | "The idea is to just group legs to view the tensor as a matrix and then perform the corresponding operation.\n", 667 | "\n", 668 | "The SVD is of particular importance because it eventually allows truncation. We can use it to calculate the entropy of the singlet:" 669 | ] 670 | }, 671 | { 672 | "cell_type": "code", 673 | "execution_count": null, 674 | "id": "6579de4d-83a0-4879-9223-cce6555566bc", 675 | "metadata": {}, 676 | "outputs": [], 677 | "source": [ 678 | "# define singlet again, same as above\n", 679 | "singlet = np.zeros([2, 2]) # i j\n", 680 | "singlet[0, 1] = 1./np.sqrt(2)\n", 681 | "singlet[1, 0] = -1./np.sqrt(2)\n", 682 | "singlet" 683 | ] 684 | }, 685 | { 686 | "cell_type": "code", 687 | "execution_count": null, 688 | "id": "8366fcdb-accc-4005-b4ab-2953840539ae", 689 | "metadata": {}, 690 | "outputs": [], 691 | "source": [ 692 | "A, S, B = np.linalg.svd(singlet, full_matrices=False) # i j -> i a, a, a j\n", 693 | "S # Schmidt values" 694 | ] 695 | }, 696 | { 697 | "cell_type": "markdown", 698 | "id": "e43c8c50-8b8a-45b0-b03a-fcf5120ea0f3", 699 | "metadata": {}, 700 | "source": [ 701 | "We can also get the original tensor back by contracting `A` with `diag(S)` and `B`" 702 | ] 703 | }, 704 | { 705 | "cell_type": "code", 706 | "execution_count": null, 707 | "id": "ddbec137-03f3-498a-87b8-1b0924e18839", 708 | "metadata": {}, 709 | "outputs": [], 710 | "source": [ 711 | "contr = np.tensordot(np.diag(S), B, axes=[1, 0]) # a [a*], [a] j -> a j\n", 712 | "contr = np.tensordot(A, contr, axes=[1, 0]) # i [a], [a] j -> i j\n", 713 | "contr" 714 | ] 715 | }, 716 | { 717 | "cell_type": "code", 718 | "execution_count": null, 719 | "id": "1a84667a-af65-413a-b896-a232b1240dd5", 720 | "metadata": {}, 721 | "outputs": [], 722 | "source": [ 723 | "np.sum(S*S) # normalization of the state implies sum(S*S) = 1" 724 | ] 725 | }, 726 | { 727 | "cell_type": "code", 728 | "execution_count": null, 729 | "id": "59c65227-6251-4379-9bc5-9e0b3ec660ac", 730 | "metadata": {}, 731 | "outputs": [], 732 | "source": [ 733 | "def entropy(sing_vals):\n", 734 | " \"\"\"Return von Neumann entropy.\"\"\"\n", 735 | " # first discard small singular values, since 0 * log(0) -> 0\n", 736 | " # but log(0) not defined\n", 737 | " S = sing_vals[sing_vals>1.e-15]\n", 738 | " S2 = S*S\n", 739 | " S2 /= np.sum(S2) # ensure normalization\n", 740 | " return - np.sum(S2 * np.log(S2))" 741 | ] 742 | }, 743 | { 744 | "cell_type": "code", 745 | "execution_count": null, 746 | "id": "df8623fe-ba50-43cc-9da2-d2a95337f238", 747 | "metadata": {}, 748 | "outputs": [], 749 | "source": [ 750 | "entropy(S) / np.log(2)" 751 | ] 752 | }, 753 | { 754 | "cell_type": "markdown", 755 | "id": "9d12f2ac-f8d3-4d93-ab5f-261836b60fea", 756 | "metadata": {}, 757 | "source": [ 758 | "## Truncation of Schmidt values\n", 759 | "\n", 760 | "Let us now look at at spin system with `L=12` sites,\n", 761 | "and consider a bipartition into the left 6 and the right 6 spins.\n", 762 | "\n", 763 | "We already know how to get a random state with the left and right legs combined:" 764 | ] 765 | }, 766 | { 767 | "cell_type": "code", 768 | "execution_count": null, 769 | "id": "b489da72-df30-45a0-baa8-b5340bba372a", 770 | "metadata": {}, 771 | "outputs": [], 772 | "source": [ 773 | "L = 12" 774 | ] 775 | }, 776 | { 777 | "cell_type": "code", 778 | "execution_count": null, 779 | "id": "c65d6cc9-2ab4-468c-8b29-62fa7d105fe3", 780 | "metadata": {}, 781 | "outputs": [], 782 | "source": [ 783 | "psi_random = random_vector([2]*L) # L legs\n", 784 | "psi_random = np.reshape(psi_random, [2**(L//2), 2**(L//2)]) # combined legs (i0.i1...i{L/2-1}) (i{L/2}...i{L})" 785 | ] 786 | }, 787 | { 788 | "cell_type": "markdown", 789 | "id": "a74c2181-857f-40cb-ad45-3b873b3497b8", 790 | "metadata": {}, 791 | "source": [ 792 | "It turns out that random vectors are highly entangled with the entanglement entropy reaching nearly the maximal value $\\frac{L}{2} \\log(2)$:" 793 | ] 794 | }, 795 | { 796 | "cell_type": "code", 797 | "execution_count": null, 798 | "id": "b6b21c51-2900-4e2e-a052-7d84241e0ab5", 799 | "metadata": {}, 800 | "outputs": [], 801 | "source": [ 802 | "A_random, S_random, B_random = np.linalg.svd(psi_random, full_matrices=False)\n", 803 | "entropy(S_random) / np.log(2)" 804 | ] 805 | }, 806 | { 807 | "cell_type": "markdown", 808 | "id": "42ef5003-bb39-4ce7-be15-b610cf6ea2fa", 809 | "metadata": {}, 810 | "source": [ 811 | "Ground states of local Hamiltonians behave very differently; in 1D the are law of the entanglement predicts a scaling constant in $L$.\n", 812 | "(An exception are critical, gapless points where CFT predicts $S \\propto \\log(L)$.)\n", 813 | "\n", 814 | "The following snippet uses exact diagonalization (and the Lanczos algorithm) to obtain the ground state of the transverse field ising model with $J=1, g=1.1$. (You can consider it a black bock for now - here we are interested in the properties of the state only.)" 815 | ] 816 | }, 817 | { 818 | "cell_type": "code", 819 | "execution_count": null, 820 | "id": "16fafae8-b956-4bc2-a8c0-17d9e4c9053e", 821 | "metadata": {}, 822 | "outputs": [], 823 | "source": [ 824 | "from tenpy_toycodes.tfi_exact import finite_gs_energy\n", 825 | "\n", 826 | "energy, psi_ground_state = finite_gs_energy(L=L, J=1., g=1.1, return_psi=True)\n", 827 | "psi_ground_state.shape" 828 | ] 829 | }, 830 | { 831 | "cell_type": "code", 832 | "execution_count": null, 833 | "id": "c08b51e3-3607-4069-966d-c69b694be19b", 834 | "metadata": {}, 835 | "outputs": [], 836 | "source": [ 837 | "psi_ground_state = np.reshape(psi_ground_state, [2**(L//2), 2**(L//2)]) # combined left and right legs\n", 838 | "A_ground_state, S_ground_state, B_ground_state = np.linalg.svd(psi_ground_state, full_matrices=False)\n", 839 | "entropy(S_ground_state) / np.log(2)" 840 | ] 841 | }, 842 | { 843 | "cell_type": "markdown", 844 | "id": "8bcedc95-0163-4d63-b553-9954e61f09e2", 845 | "metadata": {}, 846 | "source": [ 847 | "We can see this behaviour reflected in the decay of singular values:" 848 | ] 849 | }, 850 | { 851 | "cell_type": "code", 852 | "execution_count": null, 853 | "id": "e76952d4-401d-459e-9721-15241b22a6c5", 854 | "metadata": {}, 855 | "outputs": [], 856 | "source": [ 857 | "plt.figure(figsize=(4,3))\n", 858 | "plt.plot(S_random, 'o', label='random state')\n", 859 | "plt.plot(S_ground_state, 's', label='ground state')\n", 860 | "plt.yscale('log')\n", 861 | "plt.ylim(1.e-15, 1.)\n", 862 | "plt.xlim(0, 30)\n", 863 | "plt.xlabel(r'singular value index $\\alpha$')\n", 864 | "plt.ylabel(r'singular values $\\lambda_\\alpha$')\n", 865 | "plt.legend(loc='lower left')" 866 | ] 867 | }, 868 | { 869 | "cell_type": "markdown", 870 | "id": "a87a7752-b8f7-4d0e-8ed7-7c2f56f770d8", 871 | "metadata": {}, 872 | "source": [ 873 | "Ultimately, this decay of singular values for ground states is the reason why DMRG and MPS are so highly successfull.\n", 874 | "It implies that we can truncate the state to much smaller bond dimension and still have large overlap with the initial state:" 875 | ] 876 | }, 877 | { 878 | "cell_type": "code", 879 | "execution_count": null, 880 | "id": "25b44bfd-6601-483b-bba3-08cfe7962404", 881 | "metadata": {}, 882 | "outputs": [], 883 | "source": [ 884 | "chi = 5\n", 885 | "A_trunc = A_ground_state[:, :chi]\n", 886 | "S_trunc = S_ground_state[:chi]\n", 887 | "S_trunc = S_trunc / np.linalg.norm(S_trunc) # normalize to 1 again\n", 888 | "B_trunc = B_ground_state[:chi, :]\n", 889 | "psi_trunc = np.tensordot(np.diag(S_trunc), B_trunc, axes=[1, 0]) # a [a*], [a] j -> a j\n", 890 | "psi_trunc = np.tensordot(A_trunc, psi_trunc, axes=[1, 0]) # i [a], [a] j -> i j\n", 891 | "overlap = np.tensordot(np.conj(psi_trunc), psi_ground_state, axes=([0, 1], [0, 1])) # [i*] [j*], [i] [j]\n", 892 | "err = 1. - np.abs(overlap)\n", 893 | "err" 894 | ] 895 | }, 896 | { 897 | "cell_type": "markdown", 898 | "id": "a5c56399-fc49-43c6-b05d-e7e8650beaae", 899 | "metadata": {}, 900 | "source": [ 901 | "### Exercise: truncation\n", 902 | "\n", 903 | "Play around with the \"bond dimension\" `chi` of Schmidt values kept above. What is the relation between the largest Schmidt value discared and the `err` above?\n", 904 | "\n", 905 | "Try truncating the random state. What is the (absolute value of the) overlap if you keep 32 of the original 64 Schmidt values?" 906 | ] 907 | }, 908 | { 909 | "cell_type": "code", 910 | "execution_count": null, 911 | "id": "a745617f-c84f-4982-991c-aa80a0771d02", 912 | "metadata": {}, 913 | "outputs": [], 914 | "source": [] 915 | }, 916 | { 917 | "cell_type": "code", 918 | "execution_count": null, 919 | "id": "fab290ec-c0a2-4063-b5d3-3aa78cda8bb2", 920 | "metadata": {}, 921 | "outputs": [], 922 | "source": [] 923 | }, 924 | { 925 | "cell_type": "code", 926 | "execution_count": null, 927 | "id": "3fc14564-3750-4a1e-bc86-245f7e71e06d", 928 | "metadata": {}, 929 | "outputs": [], 930 | "source": [] 931 | }, 932 | { 933 | "cell_type": "markdown", 934 | "id": "344bccbb-e47a-4b63-9b69-17123b78422f", 935 | "metadata": {}, 936 | "source": [ 937 | "### Exercises (optional, if time left - for the experts, and those who want to become them)\n", 938 | "\n", 939 | "- What is the maximal entropy that one can represent with $\\chi$ Schmidt states? \n", 940 | " (You can solve this analytically!)\n", 941 | "\n", 942 | " \n" 943 | ] 944 | }, 945 | { 946 | "cell_type": "code", 947 | "execution_count": null, 948 | "id": "0b7ac8ea-84aa-49a8-8b91-bb9d4d80eb81", 949 | "metadata": {}, 950 | "outputs": [], 951 | "source": [] 952 | }, 953 | { 954 | "cell_type": "markdown", 955 | "id": "1fca3123-4735-4ae9-a296-ac92c6639768", 956 | "metadata": {}, 957 | "source": [ 958 | "- Find the half-chain entanglement entropy of the following states for differrent system sizes $L=6,8,10,12,14$ and\n", 959 | " - random states as defined above\n", 960 | " - ground states of the transferse field Ising model at $g = 1.2 J$, $g=0.8J$ and the critical point $g=J$.\n", 961 | " \n", 962 | " Plot curves of $S$ versus $L$. (Where) can you see the area law?" 963 | ] 964 | }, 965 | { 966 | "cell_type": "code", 967 | "execution_count": null, 968 | "id": "70ae8fa8-a7f4-4daf-aea7-adf7e7310aab", 969 | "metadata": {}, 970 | "outputs": [], 971 | "source": [] 972 | }, 973 | { 974 | "cell_type": "code", 975 | "execution_count": null, 976 | "id": "5e3a2b5c-166a-4d75-bcd3-d54a6e069217", 977 | "metadata": {}, 978 | "outputs": [], 979 | "source": [] 980 | } 981 | ], 982 | "metadata": { 983 | "kernelspec": { 984 | "display_name": "Python 3 (ipykernel)", 985 | "language": "python", 986 | "name": "python3" 987 | }, 988 | "language_info": { 989 | "codemirror_mode": { 990 | "name": "ipython", 991 | "version": 3 992 | }, 993 | "file_extension": ".py", 994 | "mimetype": "text/x-python", 995 | "name": "python", 996 | "nbconvert_exporter": "python", 997 | "pygments_lexer": "ipython3", 998 | "version": "3.10.6" 999 | } 1000 | }, 1001 | "nbformat": 4, 1002 | "nbformat_minor": 5 1003 | } 1004 | -------------------------------------------------------------------------------- /toycode_example_calls.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "16e0042f", 6 | "metadata": {}, 7 | "source": [ 8 | "# Example functions inside toycodes\n", 9 | "\n", 10 | "\n", 11 | "This notebook just gives further illustrations some of the toycodes can be called." 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "id": "6729423c", 17 | "metadata": {}, 18 | "source": [ 19 | "## c_tebd.py" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": 1, 25 | "id": "f293bea9", 26 | "metadata": {}, 27 | "outputs": [], 28 | "source": [ 29 | "from tenpy_toycodes import c_tebd" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": 2, 35 | "id": "3b47bb7c", 36 | "metadata": {}, 37 | "outputs": [ 38 | { 39 | "name": "stdout", 40 | "output_type": "stream", 41 | "text": [ 42 | "finite TEBD, imaginary time evolution, transverse field Ising\n", 43 | "L=10, g=1.00\n", 44 | "dt = 0.10000: E = -12.3446865032241\n", 45 | "dt = 0.01000: E = -12.3780979960037\n", 46 | "dt = 0.00100: E = -12.3811510623369\n", 47 | "dt = 0.00010: E = -12.3814538480965\n", 48 | "dt = 0.00001: E = -12.3814841409341\n", 49 | "final bond dimensions: [2, 4, 8, 14, 19, 14, 8, 4, 2]\n", 50 | "magnetization in X = 7.32205\n", 51 | "magnetization in Z = 0.00000\n", 52 | "Exact diagonalization: E = -12.3814899996547\n", 53 | "relative error: 4.731838146828168e-07\n" 54 | ] 55 | }, 56 | { 57 | "data": { 58 | "text/plain": [ 59 | "(-12.38148414093407,\n", 60 | " ,\n", 61 | " )" 62 | ] 63 | }, 64 | "execution_count": 2, 65 | "metadata": {}, 66 | "output_type": "execute_result" 67 | } 68 | ], 69 | "source": [ 70 | "c_tebd.example_TEBD_gs_tf_ising_finite(L=10, g=1.)" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": 3, 76 | "id": "908c34ab", 77 | "metadata": {}, 78 | "outputs": [ 79 | { 80 | "name": "stdout", 81 | "output_type": "stream", 82 | "text": [ 83 | "infinite TEBD, imaginary time evolution, transverse field Ising\n", 84 | "g=1.50\n", 85 | "dt = 0.10000: E (per site) = -1.6622376659950\n", 86 | "dt = 0.01000: E (per site) = -1.6709751043234\n", 87 | "dt = 0.00100: E (per site) = -1.6718311225992\n", 88 | "dt = 0.00010: E (per site) = -1.6719164301789\n", 89 | "dt = 0.00001: E (per site) = -1.6719249672500\n", 90 | "final bond dimensions: [21, 21]\n", 91 | " = 0.87733\n", 92 | " = -0.00000\n", 93 | "correlation length: 2.410718886443566\n", 94 | "Analytic result: E (per site) = -1.6719262215362\n", 95 | "relative error: 7.502042877632601e-07\n" 96 | ] 97 | } 98 | ], 99 | "source": [ 100 | "E, psi, model = c_tebd.example_TEBD_gs_tf_ising_infinite(g=1.5)" 101 | ] 102 | }, 103 | { 104 | "cell_type": "markdown", 105 | "id": "1f2787a2", 106 | "metadata": {}, 107 | "source": [ 108 | "## d_dmrg.py" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": 4, 114 | "id": "f6f30472", 115 | "metadata": {}, 116 | "outputs": [], 117 | "source": [ 118 | "from tenpy_toycodes import d_dmrg" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": 5, 124 | "id": "28eba62b", 125 | "metadata": {}, 126 | "outputs": [ 127 | { 128 | "name": "stdout", 129 | "output_type": "stream", 130 | "text": [ 131 | "finite DMRG, transverse field Ising\n", 132 | "L=10, g=1.00\n", 133 | "sweep 1: E = -12.3720222354851\n", 134 | "sweep 2: E = -12.3814900173904\n", 135 | "sweep 3: E = -12.3814899996548\n", 136 | "sweep 4: E = -12.3814899996548\n", 137 | "sweep 5: E = -12.3814899996548\n", 138 | "sweep 6: E = -12.3814899996548\n", 139 | "sweep 7: E = -12.3814899996548\n", 140 | "sweep 8: E = -12.3814899996548\n", 141 | "sweep 9: E = -12.3814899996548\n", 142 | "sweep 10: E = -12.3814899996548\n", 143 | "final bond dimensions: [2, 4, 8, 14, 19, 14, 8, 4, 2]\n", 144 | "magnetization in X = 7.32255\n", 145 | "magnetization in Z = -0.00000\n", 146 | "Exact diagonalization: E = -12.3814899996548\n", 147 | "relative error: 4.3040623691892867e-16\n" 148 | ] 149 | } 150 | ], 151 | "source": [ 152 | "E, psi, model = d_dmrg.example_DMRG_tf_ising_finite(L=10, g=1.)" 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": 6, 158 | "id": "3afe4152", 159 | "metadata": {}, 160 | "outputs": [ 161 | { 162 | "name": "stdout", 163 | "output_type": "stream", 164 | "text": [ 165 | "infinite DMRG, transverse field Ising\n", 166 | "g=1.50\n", 167 | "sweep 1: E (per site) = -1.6772694701518\n", 168 | "sweep 2: E (per site) = -1.6723228806395\n", 169 | "sweep 3: E (per site) = -1.6719671140527\n", 170 | "sweep 4: E (per site) = -1.6719314036249\n", 171 | "sweep 5: E (per site) = -1.6719269556168\n", 172 | "sweep 6: E (per site) = -1.6719263323442\n", 173 | "sweep 7: E (per site) = -1.6719262389782\n", 174 | "sweep 8: E (per site) = -1.6719262243659\n", 175 | "sweep 9: E (per site) = -1.6719262220060\n", 176 | "sweep 10: E (per site) = -1.6719262216156\n", 177 | "sweep 11: E (per site) = -1.6719262215498\n", 178 | "sweep 12: E (per site) = -1.6719262215386\n", 179 | "sweep 13: E (per site) = -1.6719262215366\n", 180 | "sweep 14: E (per site) = -1.6719262215363\n", 181 | "sweep 15: E (per site) = -1.6719262215362\n", 182 | "sweep 16: E (per site) = -1.6719262215362\n", 183 | "sweep 17: E (per site) = -1.6719262215362\n", 184 | "sweep 18: E (per site) = -1.6719262215362\n", 185 | "sweep 19: E (per site) = -1.6719262215362\n", 186 | "sweep 20: E (per site) = -1.6719262215362\n", 187 | "final bond dimensions: [30, 30]\n", 188 | " = 0.87733\n", 189 | " = 0.00000\n", 190 | "correlation length: 2.4243849345542476\n", 191 | "Analytic result: E (per site) = -1.6719262215362\n", 192 | "relative error: 6.640382872906104e-16\n" 193 | ] 194 | } 195 | ], 196 | "source": [ 197 | "E, psi, model = d_dmrg.example_DMRG_tf_ising_infinite(g=1.5)" 198 | ] 199 | }, 200 | { 201 | "cell_type": "markdown", 202 | "id": "4c31ce8e", 203 | "metadata": {}, 204 | "source": [ 205 | "## e_tdvp.py" 206 | ] 207 | }, 208 | { 209 | "cell_type": "code", 210 | "execution_count": 7, 211 | "id": "f2493949", 212 | "metadata": {}, 213 | "outputs": [], 214 | "source": [ 215 | "from tenpy_toycodes import e_tdvp" 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "execution_count": 8, 221 | "id": "5a71b3d9", 222 | "metadata": {}, 223 | "outputs": [ 224 | { 225 | "name": "stdout", 226 | "output_type": "stream", 227 | "text": [ 228 | "finite TEBD, real time evolution, transverse field Ising\n", 229 | "L=20, g=1.50, tmax=3.00, dt=0.050\n", 230 | "finite DMRG, transverse field Ising\n", 231 | "L=20, g=1.50\n", 232 | "sweep 1: E = -33.2584260524715\n", 233 | "sweep 2: E = -33.2545167807381\n", 234 | "sweep 3: E = -33.2545167536354\n", 235 | "sweep 4: E = -33.2545167536354\n", 236 | "sweep 5: E = -33.2545167536354\n", 237 | "sweep 6: E = -33.2545167536354\n", 238 | "sweep 7: E = -33.2545167536354\n", 239 | "sweep 8: E = -33.2545167536354\n", 240 | "sweep 9: E = -33.2545167536354\n", 241 | "sweep 10: E = -33.2545167536354\n", 242 | "final bond dimensions: [2, 4, 8, 13, 17, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 13, 8, 4, 2]\n", 243 | "magnetization in X = 17.69863\n", 244 | "magnetization in Z = -0.00000\n", 245 | "E after applying Sz = -30.6225268617764\n", 246 | "t = 0.00, chi = [2, 4, 8, 13, 17, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 13, 8, 4, 2]\n" 247 | ] 248 | }, 249 | { 250 | "name": "stderr", 251 | "output_type": "stream", 252 | "text": [ 253 | "/home/johannes/postdoc/tenpy_other/tenpy_toycodes/tenpy_toycodes/e_tdvp.py:242: UserWarning: Trace of LinearOperator not available, it will be estimated. Provide `traceA` to ensure performance.\n", 254 | " return expm_multiply((-1.j*dt) * H, psi0)\n" 255 | ] 256 | }, 257 | { 258 | "name": "stdout", 259 | "output_type": "stream", 260 | "text": [ 261 | "t = 0.20, chi = [2, 4, 8, 13, 17, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 13, 8, 4, 2]\n", 262 | "t = 0.40, chi = [2, 4, 8, 13, 17, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 13, 8, 4, 2]\n", 263 | "t = 0.60, chi = [2, 4, 8, 13, 17, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 13, 8, 4, 2]\n", 264 | "t = 0.80, chi = [2, 4, 8, 13, 17, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 13, 8, 4, 2]\n", 265 | "t = 1.00, chi = [2, 4, 8, 13, 17, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 13, 8, 4, 2]\n", 266 | "t = 1.20, chi = [2, 4, 8, 13, 17, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 13, 8, 4, 2]\n", 267 | "t = 1.40, chi = [2, 4, 8, 13, 17, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 13, 8, 4, 2]\n", 268 | "t = 1.60, chi = [2, 4, 8, 13, 17, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 13, 8, 4, 2]\n", 269 | "t = 1.80, chi = [2, 4, 8, 13, 17, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 13, 8, 4, 2]\n", 270 | "t = 2.00, chi = [2, 4, 8, 13, 17, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 13, 8, 4, 2]\n", 271 | "t = 2.20, chi = [2, 4, 8, 13, 17, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 13, 8, 4, 2]\n", 272 | "t = 2.40, chi = [2, 4, 8, 13, 17, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 13, 8, 4, 2]\n", 273 | "t = 2.60, chi = [2, 4, 8, 13, 17, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 13, 8, 4, 2]\n", 274 | "t = 2.80, chi = [2, 4, 8, 13, 17, 20, 20, 20, 20, 20, 20, 20, 20, 20, 17, 13, 8, 4, 2]\n", 275 | "final E = -30.6225239843235\n" 276 | ] 277 | }, 278 | { 279 | "data": { 280 | "image/png": "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7vpi2WX169cPISEhVueeffZZLF261CZtT/0e7kmaCXI6SBf1VgghbM5dKr298x2ysrKQkZFheX3q1CmEhYWhpqZGtRu2pzU3NyMkJAS1tbWqdaf2JLZH29ge7XO1NvVme4QQOH36NIYOHaraZ/j6+qK6uhpGo1F2WfZ+h9rrxfk5tX8P9yTNBDmDBw+Gp6enTbTY0NBgEyV2CAoKspvey8sLgwYNspuns246f39/l/jy/5yfn59LtYnt0Ta2R/tcrU291Z6e+IPY19cXvr7KzRO9HD31e7gnaWZGho+PD2JiYlBSUmJ1vqSkBBMnTrSbJy4uzib9jh07EBsb2+vjgERERM7EFX8PaybIAYCMjAz87W9/w/r163H48GEsXLgQNTU1SE1NBXBhqGn27NmW9KmpqTh+/DgyMjJw+PBhrF+/HgUFBcjMzOytJhARETktV/s9rJnhKgBITEzEyZMnsWzZMhgMBkRFRaG4uBhhYWEAAIPBYLVWPzw8HMXFxVi4cCFee+01DB06FK+88gruvffey/5MnU6HZ5999pJjlM7E1drE9mgb26N9rtYmV2uPlvTG72E1aeo5OURERERK0dRwFREREZFSGOQQERGRS2KQQ0RERC6JQQ4RERG5JLcIclxp2/js7Gxcf/316N+/PwICAnDXXXfhyJEjXeYpLS2FJEk2x9dff91Dte7c0qVLbeoVFBTUZR4tX5/hw4fb/b+eN2+e3fRauzZlZWW44447MHToUEiShPfff9/qfSEEli5diqFDh6JPnz646aab8NVXX12y3KKiIowZMwY6nQ5jxozB1q1bVWqBra7a1NbWhqeeegpjx45F3759MXToUMyePRs//vhjl2UWFhbavW4tLS0qt+bS1+iRRx6xqdeECRMuWW5vXaNLtcfe/7MkSXjppZc6LbM3rw9pi8sHOa62bfyuXbswb948fPbZZygpKUF7ezvi4+Nx9uzZS+Y9cuQIDAaD5bj66qt7oMaXFhkZaVWvQ4cOdZpW69fn888/t2pLx0Oy7r///i7zaeXanD17FuPGjcOrr75q9/0VK1Zg1apVePXVV/H5558jKCgIt912G06fPt1pmXv27EFiYiKSkpJw8OBBJCUlYdasWdi7d69azbDSVZvOnTuH/fv3409/+hP279+PLVu24JtvvsGdd955yXL9/PysrpnBYOiRJ9Re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DhEREbkvBjlERETkkhjkEBERkUtikENEREQuiUEOERERuSQGOUREROSSGOQQERGRS2KQQ0RERC6JQQ6Rm7rpppuQnp6uStlz587Fgw8+qErZRESXi088JnJTP/30E7y9vdG/f38AF4Ke6667Djk5OYqUrdPp0LdvX9llERE5yqu3K0BEvWPgwIFOWTYR0eXicBWRC3vvvfcwduxY9OnTB4MGDcKtt96Ks2fPArAernrkkUewa9curFmzBpIkQZIkHDt2DEIIrFixAiNGjECfPn0wbtw4vPfee11+5rFjxyBJEo4fP65284iIusSeHCIXZTAY8MADD2DFihW4++67cfr0aZSXl8PeCPWaNWvwzTffICoqCsuWLQMADBkyBH/84x+xZcsW5OXl4eqrr0ZZWRkefvhhDBkyBFOnTrX7uQcOHMCAAQMQFhamavuIiC6FQQ6RizIYDGhvb8c999xjCTjGjh1rN62/vz98fHxwxRVXICgoCABw9uxZrFq1Cp988gni4uIAACNGjMDu3buxbt26ToOcgwcPYty4cSq0iIioexjkELmocePGYdq0aRg7diymT5+O+Ph43HffffjFL35xWfmrqqrQ0tKC2267zeq80WhEdHR0p/kOHDjAIIeINIFBDpGL8vT0RElJCSoqKrBjxw789a9/xZIlS7B3716Eh4dfMr/ZbAYAbNu2DVdeeaXVezqdrtN8Bw8exJ133imv8kRECuDEYyIXJkkSJk2ahOeeew56vR4+Pj7YunWr3bQ+Pj4wmUyW12PGjIFOp0NNTQ2uuuoqqyMkJMRuGc3NzTh27Bh7cohIE9iTQ+Si9u7di3/961+Ij49HQEAA9u7di//93//F6NGj7aYfPnw49u7di2PHjqFfv34YOHAgMjMzsXDhQpjNZtx4441obm5GRUUF+vXrhzlz5tiUcfDgQXh6eiIyMlLt5hERXRKDHCIX5efnh7KyMuTk5KC5uRlhYWF4+eWXkZCQYDd9ZmYm5syZgzFjxuD8+fOorq7G888/j4CAAGRnZ+P777/HgAEDMH78eDz99NN2yzh48CBGjRrV5XAWEVFP4ROPiYiIyCVxTg4RERG5JAY5RERE5JIY5BAREZFLYpBDRERELolBDhEREbkkBjlERETkkhjkEBERkUtikENEREQuiUEOERERuSQGOUREROSSGOQQERGRS2KQQ0RERC7p/wN+Y95u0PfslQAAAABJRU5ErkJggg==\n", 281 | "text/plain": [ 282 | "
" 283 | ] 284 | }, 285 | "metadata": {}, 286 | "output_type": "display_data" 287 | } 288 | ], 289 | "source": [ 290 | "e_tdvp.example_TDVP_tf_ising_lightcone(L=20, g=1.5, tmax=3., dt=0.05, one_site=True)" 291 | ] 292 | }, 293 | { 294 | "cell_type": "code", 295 | "execution_count": null, 296 | "id": "a7a57263", 297 | "metadata": {}, 298 | "outputs": [], 299 | "source": [] 300 | }, 301 | { 302 | "cell_type": "code", 303 | "execution_count": null, 304 | "id": "a36b04f9-b015-4b19-af30-48d2c6b3b3e2", 305 | "metadata": {}, 306 | "outputs": [], 307 | "source": [] 308 | } 309 | ], 310 | "metadata": { 311 | "kernelspec": { 312 | "display_name": "Python 3 (ipykernel)", 313 | "language": "python", 314 | "name": "python3" 315 | }, 316 | "language_info": { 317 | "codemirror_mode": { 318 | "name": "ipython", 319 | "version": 3 320 | }, 321 | "file_extension": ".py", 322 | "mimetype": "text/x-python", 323 | "name": "python", 324 | "nbconvert_exporter": "python", 325 | "pygments_lexer": "ipython3", 326 | "version": "3.10.6" 327 | } 328 | }, 329 | "nbformat": 4, 330 | "nbformat_minor": 5 331 | } 332 | --------------------------------------------------------------------------------