├── dmrg101 ├── __init__.py ├── core │ ├── __init__.py │ ├── dmrg_exceptions.py │ ├── get_real.py │ ├── truncation_error.py │ ├── make_tensor.py │ ├── braket.py │ ├── calculate_states_to_keep.py │ ├── transform_matrix.py │ ├── block.py │ ├── entropies.py │ ├── reduced_DM.py │ ├── wavefunction.py │ ├── operators.py │ ├── sites.py │ ├── lanczos.py │ └── system.py └── utils │ ├── __init__.py │ ├── models │ ├── __init__.py │ ├── tfi_model.py │ ├── heisenberg_model.py │ └── hubbard_model.py │ └── tridiagonal_solver │ ├── __init__.py │ ├── example_9_13.py │ ├── tridiagonal_exceptions.py │ ├── eigenvals3.py │ ├── gerschgorin.py │ ├── LUdecomp3.py │ ├── sturmSeq.py │ ├── lamRange.py │ ├── tridiagonal_solver.py │ └── inversePower3.py ├── AUTHORS ├── version.py ├── docs ├── faq.rst ├── ref │ ├── modules.rst │ ├── dmrg101.utils.rst │ ├── dmrg101.rst │ ├── dmrg101.utils.tridiagonal_solver.rst │ └── dmrg101.core.rst ├── acknowledgements.rst ├── quick_example.rst ├── getting_the_code.rst ├── submit_a_bug.rst ├── installation.rst ├── index.rst ├── getting_started.rst ├── make.bat ├── Makefile ├── old_stuff.rst └── conf.py ├── requirements_rtd.txt ├── MANIFEST.in ├── INSTALL ├── tests ├── __init__.py └── test_entropies.py ├── .gitignore ├── requirements.txt ├── README.md ├── setup.py ├── Makefile └── LICENSE /dmrg101/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Ivan Gonzalez 2 | -------------------------------------------------------------------------------- /dmrg101/core/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dmrg101/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dmrg101/utils/models/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /version.py: -------------------------------------------------------------------------------- 1 | __version__=0.1 2 | -------------------------------------------------------------------------------- /dmrg101/utils/tridiagonal_solver/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/faq.rst: -------------------------------------------------------------------------------- 1 | ==== 2 | FAQs 3 | ==== 4 | 5 | 6 | -------------------------------------------------------------------------------- /requirements_rtd.txt: -------------------------------------------------------------------------------- 1 | numpy==1.6.1 2 | numpydoc==0.4 3 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md AUTHORS LICENSE INSTALL Makefile requirements.txt 2 | -------------------------------------------------------------------------------- /docs/ref/modules.rst: -------------------------------------------------------------------------------- 1 | dmrg101 2 | ======= 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | dmrg101 8 | -------------------------------------------------------------------------------- /docs/ref/dmrg101.utils.rst: -------------------------------------------------------------------------------- 1 | utils Package 2 | ============= 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | dmrg101.utils.tridiagonal_solver 10 | 11 | -------------------------------------------------------------------------------- /docs/ref/dmrg101.rst: -------------------------------------------------------------------------------- 1 | dmrg101 Package 2 | =============== 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | 9 | dmrg101.core 10 | dmrg101.utils 11 | 12 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | For a complete set of requirements and instructions on how to install, 2 | open in a web browser: 3 | 4 | - the file under docs/core/_build/html/installing.html, or 5 | - go to http://iglpdc.github.com/dmrg101/ 6 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | File: __init__.py 3 | Author: Ivan Gonzalez 4 | Description: Imports the dmrg101 module 5 | ''' 6 | import os 7 | import sys 8 | dmrg101_module_path=os.path.abspath('../dmrg101') 9 | sys.path.append(dmrg101_module_path) 10 | -------------------------------------------------------------------------------- /dmrg101/utils/tridiagonal_solver/example_9_13.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from numpy import ones 3 | from tridiagonal_solver import * 4 | n = 300 5 | d = ones((n))*2.0 6 | c = ones((n-1))*(-1.0) 7 | 8 | evals, evecs = tridiagonal_solver(d,c) 9 | print evals 10 | print evecs 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | 3 | # Packages 4 | *.egg 5 | *.egg-info 6 | dist 7 | build 8 | eggs 9 | parts 10 | bin 11 | var 12 | sdist 13 | develop-eggs 14 | .installed.cfg 15 | 16 | # Installer logs 17 | pip-log.txt 18 | 19 | # Unit test / coverage reports 20 | .coverage 21 | .tox 22 | 23 | #Translations 24 | *.mo 25 | 26 | #Mr Developer 27 | .mr.developer.cfg 28 | 29 | #.DS_Store 30 | .DS_Store 31 | 32 | #MANIFEST 33 | MANIFEST 34 | 35 | # The build from sphinx for both the docs and the tutorial dirs 36 | docs/_build 37 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Jinja2==2.6 2 | PyRSS2Gen==1.0.0 3 | Pygments==1.5 4 | Sphinx==1.1.3 5 | Twisted==12.0.0 6 | altgraph==0.9 7 | bdist-mpkg==0.4.4 8 | docutils==0.9.1 9 | macholib==1.4.2 10 | numpy==1.6.1 11 | numpydoc==0.4 12 | matplotlib==1.1.1 13 | modulegraph==0.9.1 14 | nose==1.2.1 15 | py2app==0.6.3 16 | pyOpenSSL==0.13 17 | python-dateutil==1.5 18 | pyzmq==2.2.0.1 19 | readline==6.2.2 20 | scipy==0.11.0 21 | tornado==2.4 22 | virtualenv==1.8.2 23 | wsgiref==0.1.2 24 | xattr==0.6.2 25 | zope.interface==3.5.1 26 | docopt 27 | -------------------------------------------------------------------------------- /dmrg101/core/dmrg_exceptions.py: -------------------------------------------------------------------------------- 1 | # 2 | # File: dmrg_exceptions.py 3 | # Author: Ivan Gonzalez 4 | # 5 | """Exception class for the DMRG code 6 | """ 7 | class DMRGException(Exception): 8 | """A base exception for the DMRG code 9 | 10 | Parameters 11 | ---------- 12 | msg : a string 13 | A message explaining the error 14 | """ 15 | def __init__(self, msg): 16 | super(DMRGException, self).__init__() 17 | self.msg = msg 18 | 19 | def __srt__(self, msg): 20 | return repr(self.msg) 21 | -------------------------------------------------------------------------------- /docs/acknowledgements.rst: -------------------------------------------------------------------------------- 1 | Acknowlegdements 2 | ---------------- 3 | 4 | We acknowlegde support from MICINN through grant FIS2009-13520 (IG). 5 | 6 | This code was developed as part of the `Taipei Density Matrix Renormalization Group 7 | Winter School `_. This school 8 | was sponsored by: 9 | 10 | - National Science Council, Taiwan, 11 | - National Center for Theoretical Sciences, Taiwan, 12 | - Center for Advanced Study in Theoretical Science, National Taiwan University, 13 | - Center for Quantum Science and Engineering, National Taiwan University. 14 | -------------------------------------------------------------------------------- /dmrg101/utils/tridiagonal_solver/tridiagonal_exceptions.py: -------------------------------------------------------------------------------- 1 | # 2 | # File: tridiagonal_exceptions.py 3 | # Author: Ivan Gonzalez 4 | # 5 | """Exception class for the tridiagonal module 6 | """ 7 | class TridiagonalException(Exception): 8 | """A base exception for the Tridiagonal module 9 | 10 | Parameters 11 | ---------- 12 | msg : a string 13 | A message explaining the error 14 | """ 15 | def __init__(self, msg): 16 | super(TridiagonalException, self).__init__() 17 | self.msg = msg 18 | 19 | def __srt__(self, msg): 20 | return repr(self.msg) 21 | -------------------------------------------------------------------------------- /docs/quick_example.rst: -------------------------------------------------------------------------------- 1 | .. quick-example_ 2 | 3 | =============== 4 | A quick example 5 | =============== 6 | 7 | If you don't know what to do, we suggest you check out some of the 8 | exercises for the school's `tutorial `_. If you need a detailed description of 9 | the implementation you can check out the `Developer Documentation`. 10 | 11 | The Heisenberg model for a spin 1/2 chain 12 | ----------------------------------------- 13 | 14 | The following code implements the Heisenberg model for a chain of spins 15 | 1/2. 16 | 17 | .. literalinclude:: ../tutorial/solutions/heisenberg_chain.py 18 | -------------------------------------------------------------------------------- /dmrg101/utils/tridiagonal_solver/eigenvals3.py: -------------------------------------------------------------------------------- 1 | ## module eigenvals3 2 | ''' lam = eigenvals3(d,c,N). 3 | Returns the N smallest eigenvalues of a 4 | tridiagonal matrix [A] = [c\d\c]. 5 | ''' 6 | import numpy as np 7 | import scipy.optimize 8 | from lamRange import * 9 | from sturmSeq import sturmSeq 10 | 11 | def eigenvals3(d,c,N): 12 | 13 | def f(x): # f(x) = |[A] - x[I]| 14 | p = sturmSeq(d,c,x) 15 | return p[len(p)-1] 16 | 17 | evals = np.empty(N) 18 | r = lamRange(d,c,N) # Bracket eigenvalues 19 | for i in range(N): # Solve by Brent's method 20 | assert( f(r[i]) * f(r[i+1]) < 0.0 ) 21 | evals[i] = scipy.optimize.brentq(f,r[i],r[i+1]) 22 | return evals 23 | -------------------------------------------------------------------------------- /dmrg101/utils/tridiagonal_solver/gerschgorin.py: -------------------------------------------------------------------------------- 1 | ## module gerschgorin 2 | ''' lamMin,lamMax = gerschgorin(d,c). 3 | Applies Gerschgorin's theorem to find the global bounds on 4 | the eigenvalues of a tridiagomal matrix [A] = [c\d\c]. 5 | ''' 6 | def gerschgorin(d,c): 7 | n = len(d) 8 | lamMin = d[0] - abs(c[0]) 9 | lamMax = d[0] + abs(c[0]) 10 | for i in range(1,n-1): 11 | lam = d[i] - abs(c[i]) - abs(c[i-1]) 12 | if lam < lamMin: lamMin = lam 13 | lam = d[i] + abs(c[i]) + abs(c[i-1]) 14 | if lam > lamMax: lamMax = lam 15 | lam = d[n-1] - abs(c[n-2]) 16 | if lam < lamMin: lamMin = lam 17 | lam = d[n-1] + abs(c[n-2]) 18 | if lam > lamMax: lamMax = lam 19 | return lamMin,lamMax 20 | -------------------------------------------------------------------------------- /docs/getting_the_code.rst: -------------------------------------------------------------------------------- 1 | .. _`Installation for developers`: 2 | 3 | =========================== 4 | Installation for developers 5 | =========================== 6 | 7 | If you are attending the School, you don't need to do this. You still have 8 | full access to the source code using the :ref:`quick-installation` method. 9 | The developer method is only for those wanting to contribute to the 10 | development of dmrg101 or want it to fork the repository. 11 | 12 | If you want to have access to the repository, you can clone it in your 13 | machine: :: 14 | 15 | $ git clone https://github.com/iglpdc/dmrg101.git 16 | 17 | This will download the latest version of dmrg101 repository. 18 | 19 | Alternatively, you can just go to the `GitHub repository of the code 20 | `_, and grab it, fork it, or whatever. 21 | -------------------------------------------------------------------------------- /dmrg101/utils/tridiagonal_solver/LUdecomp3.py: -------------------------------------------------------------------------------- 1 | ## module LUdecomp3 2 | ''' c,d,e = LUdecomp3(c,d,e). 3 | LU decomposition of tridiagonal matrix [c\d\e]. On output 4 | {c},{d} and {e} are the diagonals of the decomposed matrix. 5 | 6 | x = LUsolve3(c,d,e,b). 7 | Solves [c\d\e]{x} = {b}, where {c}, {d} and {e} are the 8 | vectors returned from LUdecomp3. 9 | ''' 10 | 11 | def LUdecomp3(c,d,e): 12 | n = len(d) 13 | for k in range(1,n): 14 | if d[k-1] == 0.0: 15 | raise Exception("Divide by zero") 16 | lam = c[k-1]/d[k-1] 17 | d[k] = d[k] - lam*e[k-1] 18 | c[k-1] = lam 19 | return c,d,e 20 | 21 | def LUsolve3(c,d,e,b): 22 | n = len(d) 23 | for k in range(1,n): 24 | b[k] = b[k] - c[k-1]*b[k-1] 25 | b[n-1] = b[n-1]/d[n-1] 26 | for k in range(n-2,-1,-1): 27 | b[k] = (b[k] - e[k]*b[k+1])/d[k] 28 | return b 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DMRG101 2 | ======= 3 | 4 | A pedagogical implementation in Python of the [Density Matrix Renormalization Group (DMRG)] (http://en.wikipedia.org/wiki/Density_matrix_renormalization_group) algorithm for the [Taipei DMRG Winter School](https://sites.google.com/site/dmrg101/). 5 | 6 | This repository hosts the materials for the tutorials on the DMRG algorithm 7 | used in the school. You will find here an implementation of the DMRG 8 | algorithm written in Python, the documentation for this code, and the 9 | tutorial itself including some exercises for the students in the school. 10 | 11 | The material is aimed to be self-content so it can be also used by people 12 | interested in learning DMRG, but who were not able to attend the school. 13 | The computer code is intended to be pedagogical, with less emphasis on 14 | performance so it's probably not useful for research purposes. 15 | 16 | For more information, take a look to the [documentation for the code and the tutorial] (http://dmrg101.rtfd.org). 17 | 18 | *Have fun!* -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from distutils.core import setup 4 | from version import __version__ 5 | 6 | setup(name='dmrg101', 7 | version=__version__, 8 | description='Pedagogical implementation of the DMRG algorithm', 9 | long_description=open('README.md').read(), 10 | author='Ivan Gonzalez', 11 | author_email='iglpdc@gmail.com', 12 | url='https://github.com/iglpdc/dmrg101', 13 | license='MIT', 14 | classifiers=[ 15 | 'Enviroment :: Console', 16 | 'Development Status :: 0 - Beta', 17 | 'Intended Audience :: Developers', 18 | 'Intended Audience :: Science/Research', 19 | 'License :: OSI Approved :: MIT license', 20 | 'Natural language :: English', 21 | 'Programming Language:: Python', 22 | 'Topic :: Scientific/Engineering', 23 | 'Topic :: Scientific/Engineering :: Physics', 24 | ], 25 | packages = ['dmrg101', 'dmrg101.core', 26 | 'dmrg101.utils', 27 | 'dmrg101.utils.tridiagonal_solver', 28 | 'dmrg101.utils.models'], 29 | py_modules = ['version'], 30 | requires = [], 31 | ) 32 | -------------------------------------------------------------------------------- /dmrg101/core/get_real.py: -------------------------------------------------------------------------------- 1 | """Gets the real part of a complex number as a double. 2 | """ 3 | def get_real(real_or_complex_number): 4 | """Gets the real part of a complex number as a double. 5 | 6 | If the argument is already a real number does nothing. It works only with 7 | numbers, not with other user defined types, such as numpy arrays. 8 | 9 | Parameters 10 | ---------- 11 | real_or_complex_number : a real or complex number. 12 | The number you want to get real. 13 | 14 | Returns 15 | ------- 16 | result : a double 17 | The real part of `real_or_complex_number`. 18 | 19 | Examples 20 | -------- 21 | >>> real_number = 1.0 22 | >>> print get_real(real_number) 23 | 1.0 24 | >>> complex_number = complex(1, 2) 25 | >>> print complex_number 26 | (1+2j) 27 | >>> print get_real(complex_number) 28 | 1.0 29 | """ 30 | result = real_or_complex_number 31 | if isinstance(real_or_complex_number, complex): 32 | result = real_or_complex_number.real 33 | return result 34 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Users can install all the code, including the docs and all dependencies 3 | # needed, by doing: 4 | # 5 | # $ make install 6 | # 7 | .PHONY: install 8 | install: 9 | pip install -r requirements.txt 10 | 11 | # 12 | # The following are intended for developers only, you don't need to use 13 | # them if just want to work with the codes. Target 'all' builds a python 14 | # distribution in one shot, performing the tests, creating all the 15 | # documentation, making a requirement file for pip, and finally packing 16 | # all together in a python distribution that you can tag and upload to 17 | # github. 18 | # 19 | .PHONY: test requires doc api-doc dist clean 20 | test: 21 | nosetests tests 22 | nosetests --with-doctest --doctest-options='+ELLIPSIS' 23 | 24 | requires: 25 | pip freeze > requirements.txt 26 | 27 | api-doc: 28 | sphinx-apidoc -o docs/ref dmrg101/ 29 | 30 | doc: api-doc 31 | cd docs && make html && cd .. 32 | 33 | dist: 34 | python setup.py sdist --formats=gztar,zip 35 | 36 | clean: 37 | rm docs/ref/*rst 38 | 39 | .PHONY : all 40 | all: test doc requires dist 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Ivan Gonzalez 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation 6 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the 8 | Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /docs/submit_a_bug.rst: -------------------------------------------------------------------------------- 1 | ===================================================== 2 | Report a problem, make a suggestion, or submit a bug 3 | ===================================================== 4 | 5 | If you want to report a problem, find a bug, have suggestions or comments, 6 | both for the code itself or on how to improve the tutorial, please fill a 7 | report using `our issue tracker 8 | `_. Please choose the proper 9 | label for your issue and provide as much information as possible, 10 | specially if you're reporting a bug. 11 | 12 | .. seealso:: 13 | 14 | `How to Report Bugs Effectively `_ 15 | Article which goes into some detail about how to create a useful bug 16 | report. This describes what kind of information is useful and why it 17 | is useful. 18 | 19 | `Bug Writing Guidelines `_ 20 | Information about writing a good bug report. Some of this is specific to 21 | the Mozilla project, but describes general good practices. 22 | -------------------------------------------------------------------------------- /dmrg101/utils/tridiagonal_solver/sturmSeq.py: -------------------------------------------------------------------------------- 1 | ## module sturmSeq 2 | ''' p = sturmSeq(c,d,lam). 3 | Returns the Sturm sequence {p[0],p[1],...,p[n]} 4 | associated with the characteristic polynomial 5 | |[A] - lam[I]| = 0, where [A] = [c\d\c] is a n x n 6 | tridiagonal matrix. 7 | 8 | numLam = numLambdas(p). 9 | Returns the number of eigenvalues of a tridiagonal 10 | matrix [A] = [c\d\c] that are smaller than 'lam'. 11 | Uses the Sturm sequence {p} obtained from 'sturmSeq'. 12 | ''' 13 | from numpy import ones 14 | 15 | def sturmSeq(d,c,lam): 16 | n = len(d) + 1 17 | p = ones(n) 18 | p[1] = d[0] - lam 19 | for i in range(2,n): 20 | ## if c[i-2] == 0.0: c[i-2] = 1.0e-12 21 | p[i] = (d[i-1] - lam)*p[i-1] - (c[i-2]**2)*p[i-2] 22 | return p 23 | 24 | def numLambdas(p): 25 | n = len(p) 26 | signOld = 1 27 | numLam = 0 28 | for i in range(1,n): 29 | if p[i] > 0.0: sign = 1 30 | elif p[i] < 0.0: sign = -1 31 | else: sign = -signOld 32 | if sign*signOld < 0: numLam = numLam + 1 33 | signOld = sign 34 | return numLam 35 | -------------------------------------------------------------------------------- /dmrg101/utils/tridiagonal_solver/lamRange.py: -------------------------------------------------------------------------------- 1 | ## module lamRange 2 | ''' r = lamRange(d,c,N). 3 | Returns the sequence {r[0],r[1],...,r[N]} that 4 | separates the N lowest eigenvalues of the tridiagonal 5 | matrix [A] = [c\d\c]; that is, r[i] < lam[i] < r[i+1]. 6 | ''' 7 | import numpy as np 8 | from sturmSeq import * 9 | from gerschgorin import * 10 | 11 | def lamRange(d, c, N): 12 | lamMin,lamMax = gerschgorin(d, c) 13 | r = np.empty(N+1) 14 | r[0] = lamMin 15 | # Search for eigenvalues in descending order 16 | for k in range(N,0,-1): 17 | # First bisection of interval(lamMin,lamMax) 18 | lam = (lamMax + lamMin)/2.0 19 | h = (lamMax - lamMin)/2.0 20 | for i in range(1000): 21 | # Find number of eigenvalues less than lam 22 | p = sturmSeq(d,c,lam) 23 | numLam = numLambdas(p) 24 | # Bisect again & find the half containing lam 25 | h = h/2.0 26 | if numLam < k: lam = lam + h 27 | elif numLam > k: lam = lam - h 28 | else: break 29 | # If eigenvalue located, change the upper limit 30 | # of search and record it in [r] 31 | lamMax = lam 32 | r[k] = lam 33 | return r 34 | -------------------------------------------------------------------------------- /dmrg101/core/truncation_error.py: -------------------------------------------------------------------------------- 1 | # 2 | # File: truncation_error.py 3 | # Author: Ivan Gonzalez 4 | # 5 | """A module to calculate the truncation error 6 | """ 7 | def calculate_truncation_error(reduced_density_matrix_evals): 8 | """Calculates the DMRG truncation error 9 | 10 | In DMRG the truncation error is defined as the sum of the 11 | eigenvalues whose eigenvectors are left out of the Hilbert space 12 | in the truncation procedure, that is: 13 | 14 | .. math:: 15 | 16 | \epsilon = 1 - \sum_{i}\lambda_{i} 17 | 18 | where :math:`\lambda_{i}` are the eigenvalues of the density matrix 19 | that are kept. 20 | 21 | Parameters 22 | ---------- 23 | reduced_density_matrix_evals : a numpy array with ndim = 1. 24 | The reduced density matrix eigenvalues *kept*. 25 | 26 | Returns 27 | ------- 28 | result : a double 29 | The truncation error 30 | 31 | Examples 32 | -------- 33 | >>> import numpy as np 34 | >>> from dmrg101.core.truncation_error import calculate_truncation_error 35 | >>> evals = np.array([0.1, 0.2, 0.3, 0.4]) 36 | >>> last_three_evals = evals[:-3] 37 | >>> truncation_error = calculate_truncation_error(last_three_evals) 38 | >>> print truncation_error 39 | 0.9 40 | """ 41 | result = 1.0-sum(reduced_density_matrix_evals) 42 | return result 43 | -------------------------------------------------------------------------------- /dmrg101/core/make_tensor.py: -------------------------------------------------------------------------------- 1 | """Makes the tensor product of matrices. 2 | """ 3 | import numpy as np 4 | 5 | def make_tensor(small_stride_matrix, large_stride_matrix): 6 | """Makes the tensor product of two matrices. 7 | 8 | The resulting matrix is made up multiplying the two matrices 9 | block-wise, such as: 10 | 11 | .. math:: 12 | result = 13 | \\begin{pmatrix} S * L_{1,1} & S * L_{1,2} & \cdots & S * L_{1,N} \\ 14 | \cdots \\ 15 | S * L_{N,1} & S * L_{N,2} & \cdots & S * L_{N,N}\\end{pmatrix} 16 | 17 | where :math:`S` stands for `small_stride_matrix`, 18 | :math:`L` stands for `large_stride_matrix`. 19 | 20 | 21 | Parameters 22 | ---------- 23 | small_stride_matrix : a numpy array of ndim = 2. 24 | The small_stride matrix for building the tensor. 25 | large_stride_matrix : a numpy array of ndim = 2. 26 | The large_stride matrix for building the tensor. 27 | 28 | Returns 29 | ------- 30 | result : a numpy array of ndim = 2. 31 | The result with sizes which are the multiples of the arguments 32 | sizes. 33 | """ 34 | small_stride_rows = small_stride_matrix.shape[0] 35 | large_stride_rows = large_stride_matrix.shape[0] 36 | small_stride_cols = small_stride_matrix.shape[1] 37 | large_stride_cols = large_stride_matrix.shape[1] 38 | cols = small_stride_cols * large_stride_cols 39 | rows = small_stride_rows * large_stride_rows 40 | 41 | result = np.empty([rows, cols]) 42 | for i in range(large_stride_rows): 43 | ii = i * small_stride_rows 44 | for j in range(large_stride_cols): 45 | jj = j * small_stride_cols 46 | result[ii : ii + small_stride_rows, 47 | jj : jj + small_stride_cols] = (small_stride_matrix * 48 | large_stride_matrix[i, j]) 49 | 50 | return result 51 | -------------------------------------------------------------------------------- /dmrg101/utils/tridiagonal_solver/tridiagonal_solver.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from tridiagonal_exceptions import TridiagonalException 3 | from lamRange import * 4 | from inversePower3 import * 5 | from eigenvals3 import * 6 | 7 | def tridiagonal_solver(d, e, eigenvectors = True): 8 | """Calculates the eigenvalues and eigenvectors of a tridiagonal and 9 | symmetric matrix. 10 | 11 | Parameters 12 | ---------- 13 | d : a numpy array with ndim = 1. 14 | The elements of the diagonal of the tridiagonal matrix. 15 | e : a numpy array with ndim = 1. 16 | The off-diagonal elements of the tridiagonal matrix. 17 | eigenvectors : a bool (optional). 18 | Whether you want to calculate the eigenvectors. 19 | 20 | Returns 21 | ------- 22 | evals : a numpy array with ndim = 1. 23 | The eigenvalues. 24 | evecs : a numpy array with ndim = 2. 25 | The eigenvectors. 26 | 27 | Raises 28 | ------ 29 | TridiagonalException 30 | if `d` and `e` have different sizes. 31 | """ 32 | if (d.size != e.size + 1): 33 | raise TridiagonalException("d, and e have different sizes") 34 | num_evals = d.size 35 | evals = np.empty(num_evals) 36 | evecs = np.empty((num_evals, num_evals)) 37 | 38 | r = lamRange(d, e, num_evals) 39 | assert(len(r) == num_evals +1) 40 | 41 | evals = eigenvals3(d, e, num_evals) 42 | 43 | if eigenvectors: 44 | for i in range(num_evals): 45 | evals[i], evecs[:, i] = inversePower3(d, e, 1.00000001*evals[i]) 46 | # 47 | # An alternative that uses the brakets from lamRange: 48 | # 49 | # if eigenvectors: 50 | # for i in range(num_evals): 51 | # s = (r[i] + r[i+1])/2.0 52 | # evals[i], evecs[:, i] = inversePower3(d, e, s) 53 | # else: 54 | # evals = eigenvals3(d, e, num_evals) 55 | 56 | return evals, evecs 57 | -------------------------------------------------------------------------------- /tests/test_entropies.py: -------------------------------------------------------------------------------- 1 | ''' 2 | File: test_entropies.py 3 | Author: Ivan Gonzalez 4 | Description: Tests for the entropy functions 5 | ''' 6 | import numpy as np 7 | import unittest 8 | from nose.tools import assert_almost_equal, with_setup, assert_true, eq_ 9 | from math import log 10 | 11 | from dmrg101.core.entropies import * 12 | 13 | class TestEntropy(unittest.TestCase): 14 | 15 | def setUp(self): 16 | self.zeros=np.array([0.0, 0.0, 0.0]) 17 | self.ones=np.array([1.0, 1.0, 1.0]) 18 | self.negatives=np.array([-1.0, -1.0, -1.0]) 19 | self.ok=np.array([0.5, 0.5]) 20 | self.ok_2=np.array([1./3, 1./3, 1./3]) 21 | self.ok_3=np.array([1./2, 1./4, 1./4]) 22 | 23 | def test_calculate_entropy(self): 24 | assert_almost_equal(calculate_entropy(self.zeros), 0.0) 25 | assert_almost_equal(calculate_entropy(self.ones), 0.0) 26 | assert_almost_equal(calculate_entropy(self.negatives), 0.0) 27 | assert_almost_equal(calculate_entropy(self.ok), log(2)) 28 | assert_almost_equal(calculate_entropy(self.ok_2), log(3)) 29 | 30 | def test_even_probabilities(self): 31 | assert_almost_equal(calculate_renyi(self.ok, 2), log(2)) 32 | assert_almost_equal(calculate_renyi(self.ok, 3), log(2)) 33 | assert_almost_equal(calculate_renyi(self.ok, 9), log(2)) 34 | 35 | def test_renyi_1_is_von_neumann(self): 36 | eq_(calculate_entropy(self.ok), calculate_renyi(self.ok, 1)) 37 | 38 | def test_are_bounded(self): 39 | assert_true(calculate_entropy(self.ok) >= calculate_renyi(self.ok, 2)) 40 | assert_true(calculate_renyi(self.ok, 2) >= calculate_renyi(self.ok, 3)) 41 | assert_true(calculate_renyi(self.ok, 3) >= calculate_renyi(self.ok, 4)) 42 | assert_true(calculate_entropy(self.ok_3) >= calculate_renyi(self.ok_3, 2)) 43 | assert_true(calculate_renyi(self.ok_3, 2) >= calculate_renyi(self.ok_3, 3)) 44 | assert_true(calculate_renyi(self.ok_3, 3) >= calculate_renyi(self.ok_3, 4)) 45 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | .. _Installation: 2 | 3 | ============ 4 | Installation 5 | ============ 6 | 7 | To use dmrg101 you need to have Python and a few Python packages installed 8 | in your machine. This section tells you how to do that. Note that there 9 | are multiple ways to install all this. We are assuming here that you are 10 | not familiar with Python, or have no Python installed at all in your machine. 11 | 12 | We describe what we think is the simplest way to have all the required 13 | packages installed. 14 | 15 | Recommended way to install the requirements 16 | ------------------------------------------- 17 | 18 | In case you don't have Python installed at all in your machine, you have 19 | basic Python installation but not all the packages required, or you tried 20 | another routes without success, you are best bet is to install the 21 | `Enthought Python Distribution 22 | `_ (EPD). 23 | 24 | The EPD is free, ships with all the stuff we need, and it has 25 | versions for MacOSX, Linux and Windows. You have two choices: 26 | 27 | - get a academic license, `EPD Academic 28 | `_ if you qualify 29 | [#]_, 30 | - get a free licence, `EPD 31 | Free `_ otherwise. 32 | 33 | The academic license has more packages (more than wnat we need for the 34 | dmrg101), but it may be a better choice if you plan to keep using Python 35 | in the future. 36 | 37 | Alternatives 38 | ------------ 39 | 40 | There are a lot of alternatives to install the software required by 41 | dmrg101, we list some for those more familiar with Python. 42 | 43 | - Windows 44 | - Linux 45 | - MacOSX 46 | 47 | 48 | 49 | -------------------------- 50 | 51 | .. [#] Basically if you are student or employee of an academic or research 52 | institution. See the `license terms 53 | `_. 54 | -------------------------------------------------------------------------------- /dmrg101/core/braket.py: -------------------------------------------------------------------------------- 1 | # 2 | # File : braket.py 3 | # Author : Ivan Gonzalez 4 | # 5 | """A module to implement quantum-mechanics brakets. 6 | """ 7 | import numpy as np 8 | from dmrg_exceptions import DMRGException 9 | 10 | def braket(bra, ket): 11 | """Takes a bra and a ket and return their braket. 12 | 13 | You use this function to calculate the quantum mechanical braket, i.e. 14 | the inner product in the wavefunction Hilbert space of two 15 | wavefunctions. 16 | 17 | The wavefunction in the bra is hermitian conjugated by the braket 18 | function. 19 | 20 | Parameters 21 | ---------- 22 | bra : a Wavefunction 23 | the bra part of the braket. 24 | ket : a Wavefunction 25 | the ket part of the braket. 26 | 27 | Returns 28 | ------- 29 | result : a double/complex depending on the type of the wavefuntions 30 | the value of the braket. 31 | 32 | Raises 33 | ------ 34 | DMRGException 35 | if the wavefunction don't belong to the same Hilbert space, 36 | i.e. they have a different number of elements. 37 | 38 | Examples 39 | -------- 40 | >>> import numpy as np 41 | >>> from dmrg101.core.wavefunction import Wavefunction 42 | >>> from dmrg101.core.braket import braket 43 | >>> bra = Wavefunction(2, 1) 44 | >>> bra.as_matrix = np.array([[ 1.], [1.]]) 45 | >>> print bra.as_matrix 46 | [[ 1.] 47 | [ 1.]] 48 | >>> ket = Wavefunction(2, 1) 49 | >>> ket.as_matrix = np.array([[ 1.], [-1.]]) 50 | >>> print ket.as_matrix 51 | [[ 1.] 52 | [-1.]] 53 | >>> print braket(bra, ket) 54 | 0.0 55 | >>> print braket(bra, bra) 56 | 2.0 57 | """ 58 | # use wf.as_matrix to access the matrix elements of wf 59 | if bra.as_matrix.shape != ket.as_matrix.shape: 60 | raise DMRGException("Wavefunctions are not in the same Hilbert space") 61 | 62 | result = np.vdot(bra.as_matrix, ket.as_matrix) 63 | return result 64 | -------------------------------------------------------------------------------- /docs/ref/dmrg101.utils.tridiagonal_solver.rst: -------------------------------------------------------------------------------- 1 | tridiagonal_solver Package 2 | ========================== 3 | 4 | :mod:`LUdecomp3` Module 5 | ----------------------- 6 | 7 | .. automodule:: dmrg101.utils.tridiagonal_solver.LUdecomp3 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | :mod:`eigenvals3` Module 13 | ------------------------ 14 | 15 | .. automodule:: dmrg101.utils.tridiagonal_solver.eigenvals3 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | :mod:`example_9_13` Module 21 | -------------------------- 22 | 23 | .. automodule:: dmrg101.utils.tridiagonal_solver.example_9_13 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | 28 | :mod:`gerschgorin` Module 29 | ------------------------- 30 | 31 | .. automodule:: dmrg101.utils.tridiagonal_solver.gerschgorin 32 | :members: 33 | :undoc-members: 34 | :show-inheritance: 35 | 36 | :mod:`inversePower3` Module 37 | --------------------------- 38 | 39 | .. automodule:: dmrg101.utils.tridiagonal_solver.inversePower3 40 | :members: 41 | :undoc-members: 42 | :show-inheritance: 43 | 44 | :mod:`lamRange` Module 45 | ---------------------- 46 | 47 | .. automodule:: dmrg101.utils.tridiagonal_solver.lamRange 48 | :members: 49 | :undoc-members: 50 | :show-inheritance: 51 | 52 | :mod:`sturmSeq` Module 53 | ---------------------- 54 | 55 | .. automodule:: dmrg101.utils.tridiagonal_solver.sturmSeq 56 | :members: 57 | :undoc-members: 58 | :show-inheritance: 59 | 60 | :mod:`tridiagonal_exceptions` Module 61 | ------------------------------------ 62 | 63 | .. automodule:: dmrg101.utils.tridiagonal_solver.tridiagonal_exceptions 64 | :members: 65 | :undoc-members: 66 | :show-inheritance: 67 | 68 | :mod:`tridiagonal_solver` Module 69 | -------------------------------- 70 | 71 | .. automodule:: dmrg101.utils.tridiagonal_solver.tridiagonal_solver 72 | :members: 73 | :undoc-members: 74 | :show-inheritance: 75 | 76 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. dmrg101 documentation master file, created by 2 | sphinx-quickstart on Wed Oct 24 11:08:10 2012. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to dmrg101's documentation! 7 | =================================== 8 | 9 | dmrg101 is an implementation in Python of the `Density Matrix 10 | Renormalization Group algorithm 11 | `_ 12 | (DMRG). It is written with a pedagogical focus, and it's probably not 13 | suitable for realistic research projects. dmrg101 was developed to be used 14 | as a tutorial code for the `Taipei Density Matrix Renormalization Group 15 | Winter School `_. 16 | 17 | The present documentation describes the use of the code *per-se*. For 18 | other information you could visit: 19 | 20 | - the `dmrg101's website `_, 21 | - the `Taipei Density Matrix Renormalization Group Winter School's website `_, 22 | - the `school's tutorial website `_. 23 | 24 | This documentation has two parts, one for users of the code and another 25 | for developers. If you are just interested in using the code to learn 26 | physics or following the tutorial for the school, you will probably need 27 | to read only the `User Documentation`_. If you are also interested in 28 | modifying the code, expand it, or just want to dig into and mess around 29 | you may want to read the `Developer Documentation`_. 30 | 31 | Unless stated explicitily otherwise, all the materials are under a 32 | :download:`MIT license<../LICENSE>`, meaning pretty much that you can use 33 | it as you want. 34 | 35 | 36 | User Documentation 37 | ------------------ 38 | .. toctree:: 39 | :maxdepth: 1 40 | 41 | getting_started 42 | installation 43 | quick_example 44 | submit_a_bug 45 | faq 46 | acknowledgements 47 | 48 | Developer Documentation 49 | ----------------------- 50 | .. toctree:: 51 | :maxdepth: 1 52 | 53 | getting_the_code 54 | ref/dmrg101 55 | -------------------------------------------------------------------------------- /dmrg101/core/calculate_states_to_keep.py: -------------------------------------------------------------------------------- 1 | # 2 | # File: calculate_states_to_keep.py 3 | # Author: Ivan Gonzalez 4 | # 5 | """A module to increase the number of states in the finite algorithm. 6 | """ 7 | from dmrg101.core.dmrg_exceptions import DMRGException 8 | 9 | def calculate_states_to_keep(initial_states, final_states, 10 | number_of_sweeps): 11 | """Increases the number of states linearly during the finite algorithm. 12 | 13 | During the finite algoeithm not all the sweeps are performed keeping 14 | the same number of states. A good way to increase the number of states 15 | linearly in each half-sweep. The last sweep is always performed at 16 | final number of states. 17 | 18 | Parameters 19 | ---------- 20 | initial_states : an int. 21 | The number of states you are keeping at the beginning of the finite 22 | algorithm, which is the same as the number of states that you kept 23 | at the end of the infinite algorithm. 24 | final_states : an int. 25 | The number of states you will keep at the end of the finite 26 | algorithm. 27 | number_of_sweeps : an int. 28 | The number of (full) sweeps during the finite algorithm. 29 | 30 | Returns 31 | ------- 32 | result : a list of ints. 33 | A list with the number of states kept at each *half-sweep*. 34 | 35 | Examples 36 | -------- 37 | >>> from dmrg101.core.calculate_states_to_keep import 38 | ... calculate_states_to_keep 39 | >>> print calculate_states_to_keep(20, 100, 5) 40 | 0.9 41 | """ 42 | result = [] 43 | if number_of_sweeps == 1: 44 | result += [final_states, final_states] 45 | elif number_of_sweeps > 1: 46 | half_sweeeps_to_increase = 2 * (number_of_sweeps - 1) 47 | step = (final_states - initial_states) / half_sweeeps_to_increase 48 | if step <= 0: 49 | raise DMRGException('Final number of states <= initial') 50 | padding = (final_states - initial_states) % half_sweeeps_to_increase 51 | result = range(initial_states+padding, final_states, step) 52 | result += [final_states, final_states] 53 | assert(len(result) == 2 * number_of_sweeps) 54 | return result 55 | -------------------------------------------------------------------------------- /docs/getting_started.rst: -------------------------------------------------------------------------------- 1 | .. Getting started for users 2 | =============== 3 | Getting started 4 | =============== 5 | 6 | dmrg101 is written in Python and to use it you need a Python interpreter. 7 | Additionally it requires a couple of Python packages containing numerical 8 | libraries. 9 | 10 | If you are attending the School, and want to have everything ready for the 11 | tutorial, please read the :ref:`Installation` page carefully. 12 | 13 | Requirements 14 | ------------ 15 | 16 | Apart from the drgm101 code itself, you need the following software 17 | installed in your computer: 18 | 19 | - Python (*which version*), 20 | - Numpy (*which version*), 21 | - optional packages: matplolib, ... 22 | 23 | You should install these requirements *prior* to get the dmrg101 code. If 24 | you need the details on how to install them for your OS, take a look to 25 | the :ref:`Installation` page. 26 | 27 | Installing dmrg101 28 | ------------------ 29 | 30 | First, you will need to install Python and the various packages required 31 | as described in the sections above. You will also need to download and 32 | install `Git `__. 33 | 34 | There are several different ways to install dmrg101, including a quick 35 | method and a developer method. For the developer method to access the 36 | repository, see the :ref:`Installation for developers`. 37 | 38 | If you are attending the School, you just need to use the quick method 39 | below. 40 | 41 | .. _quick-install: 42 | 43 | Quick installation 44 | ^^^^^^^^^^^^^^^^^^ 45 | 46 | .. note:: Depending on your setup, you may need to preface each of the ``pip ...`` 47 | commands with ``sudo pip ...``. 48 | 49 | The easiest way to install dmrg101 is to use ``pip``: :: 50 | 51 | $ pip install git+https://github.com/iglpdc/dmrg101.git 52 | 53 | This will download and install the latest version of dmrg101. To upgrade 54 | dmrg101 at a later date, you can run: :: 55 | 56 | $ pip install --upgrade git+https://github.com/iglpdc/dmrg101.git 57 | 58 | *That's it!* 59 | 60 | Testing 61 | ------- 62 | 63 | .. todo:: write something with sense here. 64 | 65 | To check that all is fine, you can try to run a few scripts: :: 66 | 67 | >>> import dmrg101 68 | 69 | 70 | -------------------------------------------------------------------------------- /dmrg101/core/transform_matrix.py: -------------------------------------------------------------------------------- 1 | # 2 | # File: transform_matrix.py 3 | # Author: Ivan Gonzalez 4 | # 5 | """ A function to transform a matrix to a new (truncated) basis. 6 | """ 7 | import numpy as np 8 | from dmrg_exceptions import DMRGException 9 | 10 | def transform_matrix(matrix_to_transform, transformation_matrix): 11 | """Transforms a matrix to a new (truncated) basis. 12 | 13 | You use this function to perform a change of basis for a given matrix. 14 | If the transformation matrix is square, it should be unitary. 15 | Otherwise, it was an unitary matrix with some columns removed (i.e. 16 | truncated.) Therefore the transformation matrix has always at least 17 | the same number of rows than columns. 18 | 19 | Parameters 20 | ---------- 21 | matrix_to_transform : a numpy array of ndim = 2. 22 | The matrix you want to transform. 23 | transform_matrix : a numpy array of ndim = 2. 24 | The transformation matrix. 25 | 26 | Returns 27 | ------- 28 | result : a numpy array of ndim = 2. 29 | The matrix transformed to the new (truncated) basis. 30 | 31 | Raises 32 | ------ 33 | DMRGException 34 | if `original_matrix` is not square, or `transformation_matrix` 35 | don't fit `original_matrix`. 36 | 37 | Examples 38 | -------- 39 | >>> import numpy as np 40 | >>> from dmrg101.core.reduced_DM import diagonalize 41 | >>> from dmrg101.core.transform_matrix import transform_matrix 42 | >>> original_matrix = np.eye(2) 43 | >>> # get a unitary matrix by diagonalizing a symmetric matrix 44 | >>> symmetric_matrix = np.array([[0.8, 0.5], 45 | ... [0.5, -0.25]]) 46 | >>> evals, evecs = diagonalize(symmetric_matrix) 47 | >>> transformed_matrix = transform_matrix(original_matrix, evecs) 48 | >>> print transformed_matrix 49 | [[ 1. 0.] 50 | [ 0. 1.]] 51 | """ 52 | if matrix_to_transform.shape[0] != matrix_to_transform.shape[1]: 53 | raise DMRGException("Cannot transform a non-square matrix") 54 | if matrix_to_transform.shape[0] != transformation_matrix.shape[0]: 55 | raise DMRGException("Matrix and transformation don't fit") 56 | tmp = np.dot(matrix_to_transform, transformation_matrix) 57 | return np.dot(np.conj(transformation_matrix.transpose()), tmp) 58 | -------------------------------------------------------------------------------- /dmrg101/utils/tridiagonal_solver/inversePower3.py: -------------------------------------------------------------------------------- 1 | # 2 | # File: tridiagonal_exceptions.py 3 | # Author: Ivan Gonzalez 4 | # 5 | """Inverse power method applied to a tridiagonal matrix. 6 | 7 | Adapted from Jaan Kiusalaas, Numerical Methods in Engineering with Python. 8 | """ 9 | import numpy as np 10 | from LUdecomp3 import * 11 | from tridiagonal_exceptions import TridiagonalException 12 | 13 | def inversePower3(d, c, s, tol=1.0e-6, max_iterations=100): 14 | """ Inverse power method for triagonal matrix. 15 | 16 | Inverse power method applied to a tridiagonal matrix 17 | [A] = [c\d\c]. Returns the eigenvalue closest to 's' 18 | and the corresponding eigenvector. 19 | 20 | Parameters 21 | ---------- 22 | d : a numpy array of ndim = 1. 23 | The matrix in the diagonal. 24 | c : a numpy array of ndim = 1. 25 | The matrix in the second-diagonal. 26 | s : a double. 27 | The eigenvalue you want to find its eigenvector. This is an 28 | approximate value. 29 | tol : a double (optional). 30 | The overlap between consecutive solutions has to be smaller than 31 | that to call the eigenvector converged. 32 | max_iterations : an int. 33 | The maximum number of iteration done before raising an exception. 34 | 35 | Returns 36 | ------- 37 | s : a double. 38 | The eigenvalue corrected. 39 | x : a numpy array of ndim = 1. 40 | The corresponding eigenvector. 41 | 42 | Raises 43 | ------ 44 | DMRGException 45 | if the number of iterations before converging exceeds 46 | `max_iterations`. 47 | """ 48 | n = len(d) 49 | e = c.copy() 50 | cc = c.copy() # Save original [c] 51 | dStar = d - s # Form [A*] = [A] - s[I] 52 | LUdecomp3(cc,dStar,e) # Decompose [A*] 53 | x = np.random.rand(n) # Seed [x] with random numbers 54 | norm = np.linalg.norm(x) # Normalize [x] 55 | x /= norm 56 | is_converged = False 57 | iteration = 0 58 | sign = 1.0 59 | while not is_converged: # Begin iterations 60 | iteration +=1 61 | if iteration > max_iterations: 62 | raise TridiagonalException('Inverse power method did not converge') 63 | xOld = x.copy() # Save current [x] 64 | LUsolve3(cc,dStar,e,x) # Solve [A*][x] = [xOld] 65 | norm = np.linalg.norm(x) # Normalize [x] 66 | x /= norm 67 | if np.dot(xOld,x) < 0.0: # Detect change in sign of [x] 68 | sign = -1.0 69 | x = -x 70 | overlap = np.linalg.norm(xOld - x) 71 | if overlap < tol: 72 | is_converged =True 73 | s += sign/norm 74 | return s, x 75 | -------------------------------------------------------------------------------- /docs/ref/dmrg101.core.rst: -------------------------------------------------------------------------------- 1 | core Package 2 | ============ 3 | 4 | :mod:`block` Module 5 | ------------------- 6 | 7 | .. automodule:: dmrg101.core.block 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | :mod:`braket` Module 13 | -------------------- 14 | 15 | .. automodule:: dmrg101.core.braket 16 | :members: 17 | :undoc-members: 18 | :show-inheritance: 19 | 20 | :mod:`dmrg_exceptions` Module 21 | ----------------------------- 22 | 23 | .. automodule:: dmrg101.core.dmrg_exceptions 24 | :members: 25 | :undoc-members: 26 | :show-inheritance: 27 | 28 | :mod:`entropies` Module 29 | ----------------------- 30 | 31 | .. automodule:: dmrg101.core.entropies 32 | :members: 33 | :undoc-members: 34 | :show-inheritance: 35 | 36 | :mod:`get_real` Module 37 | ---------------------- 38 | 39 | .. automodule:: dmrg101.core.get_real 40 | :members: 41 | :undoc-members: 42 | :show-inheritance: 43 | 44 | :mod:`lanczos` Module 45 | --------------------- 46 | 47 | .. automodule:: dmrg101.core.lanczos 48 | :members: 49 | :undoc-members: 50 | :show-inheritance: 51 | 52 | :mod:`make_tensor` Module 53 | ------------------------- 54 | 55 | .. automodule:: dmrg101.core.make_tensor 56 | :members: 57 | :undoc-members: 58 | :show-inheritance: 59 | 60 | :mod:`operators` Module 61 | ----------------------- 62 | 63 | .. automodule:: dmrg101.core.operators 64 | :members: 65 | :undoc-members: 66 | :show-inheritance: 67 | 68 | :mod:`reduced_DM` Module 69 | ------------------------ 70 | 71 | .. automodule:: dmrg101.core.reduced_DM 72 | :members: 73 | :undoc-members: 74 | :show-inheritance: 75 | 76 | :mod:`sites` Module 77 | ------------------- 78 | 79 | .. automodule:: dmrg101.core.sites 80 | :members: 81 | :undoc-members: 82 | :show-inheritance: 83 | 84 | :mod:`system` Module 85 | -------------------- 86 | 87 | .. automodule:: dmrg101.core.system 88 | :members: 89 | :undoc-members: 90 | :show-inheritance: 91 | 92 | :mod:`transform_matrix` Module 93 | ------------------------------ 94 | 95 | .. automodule:: dmrg101.core.transform_matrix 96 | :members: 97 | :undoc-members: 98 | :show-inheritance: 99 | 100 | :mod:`truncation_error` Module 101 | ------------------------------ 102 | 103 | .. automodule:: dmrg101.core.truncation_error 104 | :members: 105 | :undoc-members: 106 | :show-inheritance: 107 | 108 | :mod:`wavefunction` Module 109 | -------------------------- 110 | 111 | .. automodule:: dmrg101.core.wavefunction 112 | :members: 113 | :undoc-members: 114 | :show-inheritance: 115 | 116 | :mod:`wavefunction_transformation` Module 117 | ----------------------------------------- 118 | 119 | .. automodule:: dmrg101.core.wavefunction_transformation 120 | :members: 121 | :undoc-members: 122 | :show-inheritance: 123 | 124 | -------------------------------------------------------------------------------- /dmrg101/utils/models/tfi_model.py: -------------------------------------------------------------------------------- 1 | """A few convenience functions to setup the Ising model in a TF. 2 | 3 | TFIM stands for Ising model in a transverse field, i.e.: 4 | 5 | .. math:: 6 | H=\sum_{i}\left[S^{z}_{i}S^{z}_{i+1} + h S^{x}_{i}\right)\right] 7 | """ 8 | class TranverseFieldIsingModel(object): 9 | """Implements a few convenience functions for the TFIM. 10 | 11 | Does exactly that. 12 | """ 13 | def __init__(self, H = 0): 14 | super(TranverseFieldIsingModel, self).__init__() 15 | self.H = H 16 | 17 | def set_hamiltonian(self, system): 18 | """Sets a system Hamiltonian to the TFIM Hamiltonian. 19 | 20 | Does exactly this. If the system hamiltonian has some other terms on 21 | it, there are not touched. So be sure to use this function only in 22 | newly created `System` objects. 23 | 24 | Parameters 25 | ---------- 26 | system : a System. 27 | The System you want to set the Hamiltonain for. 28 | """ 29 | system.clear_hamiltonian() 30 | if 'bh' in system.left_block.operators.keys(): 31 | system.add_to_hamiltonian(left_block_op='bh') 32 | if 'bh' in system.right_block.operators.keys(): 33 | system.add_to_hamiltonian(right_block_op='bh') 34 | system.add_to_hamiltonian('id', 'id', 's_z', 's_z', -1.) 35 | system.add_to_hamiltonian('id', 's_z', 's_z', 'id', -1.) 36 | system.add_to_hamiltonian('s_z', 's_z', 'id', 'id', -1.) 37 | system.add_to_hamiltonian('id', 'id', 'id', 's_x', self.H) 38 | system.add_to_hamiltonian('id', 'id', 's_x', 'id', self.H) 39 | system.add_to_hamiltonian('id', 's_x', 'id', 'id', self.H) 40 | system.add_to_hamiltonian('s_x', 'id', 'id', 'id', self.H) 41 | 42 | def set_block_hamiltonian(self, tmp_matrix_for_bh, system): 43 | """Sets the block Hamiltonian to be what you need for TFIM. 44 | 45 | Parameters 46 | ---------- 47 | tmp_matrix_for_bh : a numpy array of ndim = 2. 48 | An auxiliary matrix to keep track of the result. 49 | system : a System. 50 | The System you want to set the Hamiltonian for. 51 | """ 52 | # If you have a block hamiltonian in your block, add it 53 | if 'bh' in system.growing_block.operators.keys(): 54 | system.add_to_block_hamiltonian(tmp_matrix_for_bh, 'bh', 'id') 55 | system.add_to_block_hamiltonian(tmp_matrix_for_bh, 's_z', 's_z', -1.) 56 | system.add_to_block_hamiltonian(tmp_matrix_for_bh, 'id', 's_x', self.H) 57 | system.add_to_block_hamiltonian(tmp_matrix_for_bh, 's_x', 'id', self.H) 58 | 59 | def set_operators_to_update(self, system): 60 | """Sets the operators to update to be what you need to TFIM. 61 | 62 | Parameters 63 | ---------- 64 | system : a System. 65 | The System you want to set the Hamiltonian for. 66 | 67 | Notes 68 | ----- 69 | The block Hamiltonian, althought needs to be updated, is treated 70 | separately by the very functions in the `System` class. 71 | """ 72 | system.add_to_operators_to_update('s_z', site_op='s_z') 73 | system.add_to_operators_to_update('s_x', site_op='s_x') 74 | -------------------------------------------------------------------------------- /dmrg101/utils/models/heisenberg_model.py: -------------------------------------------------------------------------------- 1 | """A few convenience functions to setup the Heisenberg model. 2 | 3 | .. math:: 4 | H=\sum_{i}\vec{S}_{i}\cdot\vec{S}_{i+1}= 5 | \sum_{i}\left[S^{z}_{i}S^{z}_{i+1}+ 6 | \frac{1}{2}\left(S^{\dagger}_{i}S^{-}_{i+1}+ 7 | S^{-}_{i}S^{\dagger}_{i+1}\right)\right] 8 | 9 | """ 10 | class HeisenbergModel(object): 11 | """Implements a few convenience functions for AF Heisenberg. 12 | 13 | Does exactly that. 14 | """ 15 | def __init__(self): 16 | super(HeisenbergModel, self).__init__() 17 | 18 | def set_hamiltonian(self, system): 19 | """Sets a system Hamiltonian to the AF Heisenberg Hamiltonian. 20 | 21 | Does exactly this. If the system hamiltonian has some other terms on 22 | it, there are not touched. So be sure to use this function only in 23 | newly created `System` objects. 24 | 25 | Parameters 26 | ---------- 27 | system : a System. 28 | The System you want to set the Hamiltonain for. 29 | """ 30 | system.clear_hamiltonian() 31 | if 'bh' in system.left_block.operators.keys(): 32 | system.add_to_hamiltonian(left_block_op='bh') 33 | if 'bh' in system.right_block.operators.keys(): 34 | system.add_to_hamiltonian(right_block_op='bh') 35 | system.add_to_hamiltonian('id', 'id', 's_z', 's_z') 36 | system.add_to_hamiltonian('id', 'id', 's_p', 's_m', .5) 37 | system.add_to_hamiltonian('id', 'id', 's_m', 's_p', .5) 38 | system.add_to_hamiltonian('id', 's_z', 's_z', 'id') 39 | system.add_to_hamiltonian('id', 's_p', 's_m', 'id', .5) 40 | system.add_to_hamiltonian('id', 's_m', 's_p', 'id', .5) 41 | system.add_to_hamiltonian('s_z', 's_z', 'id', 'id') 42 | system.add_to_hamiltonian('s_p', 's_m', 'id', 'id', .5) 43 | system.add_to_hamiltonian('s_m', 's_p', 'id', 'id', .5) 44 | 45 | def set_block_hamiltonian(self, tmp_matrix_for_bh, system): 46 | """Sets the block Hamiltonian to be what you need for AF Heisenberg. 47 | 48 | Parameters 49 | ---------- 50 | tmp_matrix_for_bh : a numpy array of ndim = 2. 51 | An auxiliary matrix to keep track of the result. 52 | system : a System. 53 | The System you want to set the Hamiltonian for. 54 | """ 55 | # If you have a block hamiltonian in your block, add it 56 | if 'bh' in system.growing_block.operators.keys(): 57 | system.add_to_block_hamiltonian(tmp_matrix_for_bh, 'bh', 'id') 58 | system.add_to_block_hamiltonian(tmp_matrix_for_bh, 's_z', 's_z') 59 | system.add_to_block_hamiltonian(tmp_matrix_for_bh, 's_p', 's_m', .5) 60 | system.add_to_block_hamiltonian(tmp_matrix_for_bh, 's_m', 's_p', .5) 61 | 62 | def set_operators_to_update(self, system): 63 | """Sets the operators to update to be what you need to AF Heisenberg. 64 | 65 | Parameters 66 | ---------- 67 | system : a System. 68 | The System you want to set the Hamiltonian for. 69 | 70 | Notes 71 | ----- 72 | The block Hamiltonian, althought needs to be updated, is treated 73 | separately by the very functions in the `System` class. 74 | """ 75 | system.add_to_operators_to_update('s_z', site_op='s_z') 76 | system.add_to_operators_to_update('s_p', site_op='s_p') 77 | system.add_to_operators_to_update('s_m', site_op='s_m') 78 | -------------------------------------------------------------------------------- /dmrg101/core/block.py: -------------------------------------------------------------------------------- 1 | # 2 | # File: block.py 3 | # Author: Ivan Gonzalez 4 | # 5 | """ A module for blocks. 6 | """ 7 | import copy 8 | import numpy as np 9 | from dmrg101.core.dmrg_exceptions import DMRGException 10 | from dmrg101.core.sites import Site 11 | 12 | class Block(Site): 13 | """A block. 14 | 15 | That is the representation of the Hilbert space and operators of a 16 | direct product of single site's Hilbert space and operators, that have 17 | been truncated. 18 | 19 | You use this class to create the two blocks (one for the left, one for 20 | the right) needed in the DMRG algorithm. The block comes empty. 21 | 22 | Parameters 23 | ---------- 24 | dim : an int. 25 | Size of the Hilbert space. The dimension must be at least 1. A 26 | block of dim = 1 represents the vaccum (or something strange like 27 | that, it's used for demo purposes mostly.) 28 | operators : a dictionary of string and numpy array (with ndim = 2). 29 | Operators for the block. 30 | 31 | Examples 32 | -------- 33 | >>> from dmrg101.core.block import Block 34 | >>> brand_new_block = Block(2) 35 | >>> # the Hilbert space has dimension 2 36 | >>> print brand_new_block.dim 37 | 2 38 | >>> # the only operator is the identity 39 | >>> print brand_new_block.operators 40 | {'id': array([[ 1., 0.], 41 | [ 0., 1.]])} 42 | """ 43 | def __init__(self, dim): 44 | """Creates an empty block of dimension dim. 45 | 46 | Raises 47 | ------ 48 | DMRGException 49 | if `dim` < 1. 50 | 51 | Notes 52 | ----- 53 | Postcond : The identity operator (ones in the diagonal, zeros elsewhere) 54 | is added to the `self.operators` dictionary. A full of zeros block 55 | Hamiltonian operator is added to the list. 56 | """ 57 | super(Block, self).__init__(dim) 58 | 59 | def make_block_from_site(site): 60 | """Makes a brand new block using a single site. 61 | 62 | You use this function at the beginning of the DMRG algorithm to 63 | upgrade a single site to a block. 64 | 65 | Parameters 66 | ---------- 67 | site : a Site object. 68 | The site you want to upgrade. 69 | 70 | Returns 71 | ------- 72 | result: a Block object. 73 | A brand new block with the same contents that the single site. 74 | 75 | Postcond 76 | -------- 77 | The list for the operators in the site and the block are copied, 78 | meaning that the list are different and modifying the block won't 79 | modify the site. 80 | 81 | Examples 82 | -------- 83 | >>> from dmrg101.core.block import Block 84 | >>> from dmrg101.core.block import make_block_from_site 85 | >>> from dmrg101.core.sites import SpinOneHalfSite 86 | >>> spin_one_half_site = SpinOneHalfSite() 87 | >>> brand_new_block = make_block_from_site(spin_one_half_site) 88 | >>> # check all it's what you expected 89 | >>> print brand_new_block.dim 90 | 2 91 | >>> print brand_new_block.operators.keys() 92 | ['s_p', 's_z', 's_m', 'id'] 93 | >>> print brand_new_block.operators['s_z'] 94 | [[-0.5 0. ] 95 | [ 0. 0.5]] 96 | >>> print brand_new_block.operators['s_p'] 97 | [[ 0. 0.] 98 | [ 1. 0.]] 99 | >>> print brand_new_block.operators['s_m'] 100 | [[ 0. 1.] 101 | [ 0. 0.]] 102 | >>> # operators for site and block are different objects 103 | >>> print ( id(spin_one_half_site.operators['s_z']) == 104 | ... id(brand_new_block.operators['s_z']) ) 105 | False 106 | """ 107 | result = Block(site.dim) 108 | result.operators = copy.deepcopy(site.operators) 109 | return result 110 | -------------------------------------------------------------------------------- /dmrg101/core/entropies.py: -------------------------------------------------------------------------------- 1 | # 2 | # File: entropies.py 3 | # Author: Ivan Gonzalez 4 | # 5 | """Functions to calculate entanglement entropies 6 | """ 7 | from math import log 8 | from sys import float_info 9 | from numpy import vectorize, power 10 | 11 | def calculate_xlogx(x, epsilon): 12 | """Calculates :math:`x\log x` of the argument 13 | 14 | This is a little helper for the calculate_entropy function. 15 | It just does what it says. 16 | 17 | Parameters 18 | ---------- 19 | x : a double 20 | the argument 21 | epsilon : a double 22 | a cut-off to avoid the log going to minus infty. 23 | 24 | Returns 25 | ------- 26 | result : a double 27 | xlog(x) if x > epsilon, otherwise returns 0.0. 28 | 29 | Examples 30 | -------- 31 | >>> from dmrg101.core.entropies import calculate_xlogx 32 | >>> from sys import float_info 33 | >>> eigenval_ok = 0.5 34 | >>> print calculate_xlogx(eigenval_ok, float_info.epsilon) 35 | -0.34657359028 36 | >>> eigenval_too_small = 0.0 37 | >>> print calculate_xlogx(eigenval_too_small, float_info.epsilon) 38 | 0.0 39 | """ 40 | result = 0.0 41 | if x > epsilon: 42 | result = x * log(x) 43 | return result 44 | 45 | def calculate_entropy(reduced_density_matrix_evals): 46 | """Calculates the Von Neumann entanglement entropy 47 | 48 | You use this function to calculate the Von Neumann entanglement 49 | entropy for a given set of reduced density matrix evals. The 50 | function does not care whether you truncate or not the reduced 51 | density matrix, it just gives the result for the evals you pass it. 52 | 53 | The Von Neumann entanglement entropy is defined as: 54 | 55 | .. math:: 56 | S_{vN}=-\sum_{i}\lambda_{i}ln\lambda{i} 57 | 58 | where :math:`\lambda_{i}` are the eigenvalues of the reduced density matrix. 59 | 60 | Parameters 61 | ---------- 62 | reduced_density_matrix_evals : an numpy array 63 | The eigenvalues (or some of them) of the reduced density matrix. 64 | 65 | Returns 66 | ------- 67 | result : a double 68 | The value of the entropy 69 | 70 | Examples 71 | -------- 72 | >>> from dmrg101.core.entropies import calculate_entropy 73 | >>> import numpy as np 74 | >>> reduced_DM_evals = np.array([0.5, 0.5]) 75 | >>> s_vN = calculate_entropy(reduced_DM_evals) 76 | >>> print s_vN 77 | 0.69314718056 78 | """ 79 | vec_xlogx = vectorize(calculate_xlogx) 80 | result = -sum(vec_xlogx(reduced_density_matrix_evals, 81 | float_info.epsilon)) 82 | return result 83 | 84 | def calculate_renyi(reduced_density_matrix_evals, n=2): 85 | """Calculates the n-th Renyi entropy 86 | 87 | You use this function to calculate the n-th Renyi entanglement entropy 88 | entropy for a given set of reduced density matrix evals. The 89 | function does not care whether you truncate or not the reduced 90 | density matrix. 91 | 92 | The n-th Renyi entanglement entropy is defined as: 93 | 94 | .. math:: 95 | S_{n}=\\frac{1}{1-n}ln\left(\sum_{i}\lambda^{n}_{i}\\right) 96 | 97 | where :math:`\lambda_{i}` are the eigenvalues of the reduced density matrix. 98 | 99 | The value for n = 1 corresponds to the Von Neumann entanglement 100 | entropy. 101 | 102 | Parameters 103 | ---------- 104 | reduced_density_matrix_evals : an numpy array 105 | The eigenvalues of the reduced density matrix. 106 | n : an int 107 | The order of the Renyi entropy. 108 | 109 | Returns 110 | ------- 111 | result : a double 112 | The value of the entropy. 113 | 114 | Examples 115 | -------- 116 | >>> from dmrg101.core.entropies import calculate_renyi 117 | >>> import numpy as np 118 | >>> reduced_DM_evals = np.array([0.5, 0.5]) 119 | >>> s_2 = calculate_renyi(reduced_DM_evals) 120 | >>> print s_2 121 | 0.69314718056 122 | >>> s_3 = calculate_renyi(reduced_DM_evals, 3) 123 | >>> print s_3 124 | 0.69314718056 125 | """ 126 | result = 0.0 127 | if n == 1: 128 | result = calculate_entropy(reduced_density_matrix_evals) 129 | else: 130 | result = log(sum(power(reduced_density_matrix_evals, n))) 131 | result /= (1.0-n) 132 | return result 133 | -------------------------------------------------------------------------------- /dmrg101/utils/models/hubbard_model.py: -------------------------------------------------------------------------------- 1 | """A few convenience functions to setup the Hubbard model. 2 | 3 | .. math:: 4 | H=\sum_{i}\vec{S}_{i}\cdot\vec{S}_{i+1}= 5 | \sum_{i}\left[S^{z}_{i}S^{z}_{i+1}+ 6 | \frac{1}{2}\left(S^{\dagger}_{i}S^{-}_{i+1}+ 7 | S^{-}_{i}S^{\dagger}_{i+1}\right)\right] 8 | 9 | """ 10 | class HubbardModel(object): 11 | """Implements a few convenience functions for Hubbard model. 12 | 13 | Does exactly that. 14 | """ 15 | def __init__(self): 16 | super(HubbardModel, self).__init__() 17 | self.U = 0. 18 | 19 | def set_hamiltonian(self, system): 20 | """Sets a system Hamiltonian to the Hubbard Hamiltonian. 21 | 22 | Does exactly this. If the system hamiltonian has some other terms on 23 | it, there are not touched. So be sure to use this function only in 24 | newly created `System` objects. 25 | 26 | Parameters 27 | ---------- 28 | system : a System. 29 | The System you want to set the Hamiltonian for. 30 | """ 31 | system.clear_hamiltonian() 32 | if 'bh' in system.left_block.operators.keys(): 33 | system.add_to_hamiltonian(left_block_op='bh') 34 | if 'bh' in system.right_block.operators.keys(): 35 | system.add_to_hamiltonian(right_block_op='bh') 36 | system.add_to_hamiltonian('c_up', 'c_up_dag', 'id', 'id', -1.) 37 | system.add_to_hamiltonian('c_up_dag', 'c_up', 'id', 'id', -1.) 38 | system.add_to_hamiltonian('c_down', 'c_down_dag', 'id', 'id', -1.) 39 | system.add_to_hamiltonian('c_down_dag', 'c_down', 'id', 'id', -1.) 40 | system.add_to_hamiltonian('id', 'c_up', 'c_up_dag', 'id', -1.) 41 | system.add_to_hamiltonian('id', 'c_up_dag', 'c_up', 'id', -1.) 42 | system.add_to_hamiltonian('id', 'c_down', 'c_down_dag', 'id', -1.) 43 | system.add_to_hamiltonian('id', 'c_down_dag', 'c_down', 'id', -1.) 44 | system.add_to_hamiltonian('id', 'id', 'c_up', 'c_up_dag', -1.) 45 | system.add_to_hamiltonian('id', 'id', 'c_up_dag', 'c_up', -1.) 46 | system.add_to_hamiltonian('id', 'id', 'c_down', 'c_down_dag', -1.) 47 | system.add_to_hamiltonian('id', 'id', 'c_down_dag', 'c_down', -1.) 48 | system.add_to_hamiltonian('u', 'id', 'id', 'id', self.U) 49 | system.add_to_hamiltonian('id', 'u', 'id', 'id', self.U) 50 | system.add_to_hamiltonian('id', 'id', 'u', 'id', self.U) 51 | system.add_to_hamiltonian('id', 'id', 'id', 'u', self.U) 52 | 53 | def set_block_hamiltonian(self, tmp_matrix_for_bh, system): 54 | """Sets the block Hamiltonian to the Hubbard model block Hamiltonian. 55 | 56 | Parameters 57 | ---------- 58 | tmp_matrix_for_bh : a numpy array of ndim = 2. 59 | An auxiliary matrix to keep track of the result. 60 | system : a System. 61 | The System you want to set the Hamiltonian for. 62 | """ 63 | # If you have a block hamiltonian in your block, add it 64 | if 'bh' in system.growing_block.operators.keys(): 65 | system.add_to_block_hamiltonian(tmp_matrix_for_bh, 'bh', 'id') 66 | system.add_to_block_hamiltonian(tmp_matrix_for_bh, 'c_up', 'c_up_dag', -1.) 67 | system.add_to_block_hamiltonian(tmp_matrix_for_bh, 'c_up_dag', 'c_up', -1.) 68 | system.add_to_block_hamiltonian(tmp_matrix_for_bh, 'c_down', 'c_down_dag', -1.) 69 | system.add_to_block_hamiltonian(tmp_matrix_for_bh, 'c_down_dag', 'c_down', -1.) 70 | system.add_to_block_hamiltonian(tmp_matrix_for_bh, 'id', 'u', self.U) 71 | system.add_to_block_hamiltonian(tmp_matrix_for_bh, 'u', 'id', self.U) 72 | 73 | def set_operators_to_update(self, system): 74 | """Sets the operators to update to the ones for the Hubbard model. 75 | 76 | Parameters 77 | ---------- 78 | system : a System. 79 | The System you want to set the Hamiltonian for. 80 | 81 | Notes 82 | ----- 83 | The block Hamiltonian, althought needs to be updated, is treated 84 | separately by the very functions in the `System` class. 85 | """ 86 | system.add_to_operators_to_update('c_up', site_op='c_up') 87 | system.add_to_operators_to_update('c_up_dag', site_op='c_up_dag') 88 | system.add_to_operators_to_update('c_down', site_op='c_down') 89 | system.add_to_operators_to_update('c_down_dag', site_op='c_down_dag') 90 | system.add_to_operators_to_update('u', site_op='u') 91 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. linkcheck to check all external links for integrity 37 | echo. doctest to run all doctests embedded in the documentation if enabled 38 | goto end 39 | ) 40 | 41 | if "%1" == "clean" ( 42 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 43 | del /q /s %BUILDDIR%\* 44 | goto end 45 | ) 46 | 47 | if "%1" == "html" ( 48 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 49 | if errorlevel 1 exit /b 1 50 | echo. 51 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 52 | goto end 53 | ) 54 | 55 | if "%1" == "dirhtml" ( 56 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 57 | if errorlevel 1 exit /b 1 58 | echo. 59 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 60 | goto end 61 | ) 62 | 63 | if "%1" == "singlehtml" ( 64 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 68 | goto end 69 | ) 70 | 71 | if "%1" == "pickle" ( 72 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished; now you can process the pickle files. 76 | goto end 77 | ) 78 | 79 | if "%1" == "json" ( 80 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished; now you can process the JSON files. 84 | goto end 85 | ) 86 | 87 | if "%1" == "htmlhelp" ( 88 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can run HTML Help Workshop with the ^ 92 | .hhp project file in %BUILDDIR%/htmlhelp. 93 | goto end 94 | ) 95 | 96 | if "%1" == "qthelp" ( 97 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 98 | if errorlevel 1 exit /b 1 99 | echo. 100 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 101 | .qhcp project file in %BUILDDIR%/qthelp, like this: 102 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\dmrg101.qhcp 103 | echo.To view the help file: 104 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\dmrg101.ghc 105 | goto end 106 | ) 107 | 108 | if "%1" == "devhelp" ( 109 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 110 | if errorlevel 1 exit /b 1 111 | echo. 112 | echo.Build finished. 113 | goto end 114 | ) 115 | 116 | if "%1" == "epub" ( 117 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 118 | if errorlevel 1 exit /b 1 119 | echo. 120 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 121 | goto end 122 | ) 123 | 124 | if "%1" == "latex" ( 125 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 129 | goto end 130 | ) 131 | 132 | if "%1" == "text" ( 133 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The text files are in %BUILDDIR%/text. 137 | goto end 138 | ) 139 | 140 | if "%1" == "man" ( 141 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 145 | goto end 146 | ) 147 | 148 | if "%1" == "texinfo" ( 149 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 150 | if errorlevel 1 exit /b 1 151 | echo. 152 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 153 | goto end 154 | ) 155 | 156 | if "%1" == "gettext" ( 157 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 158 | if errorlevel 1 exit /b 1 159 | echo. 160 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 161 | goto end 162 | ) 163 | 164 | if "%1" == "changes" ( 165 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 166 | if errorlevel 1 exit /b 1 167 | echo. 168 | echo.The overview file is in %BUILDDIR%/changes. 169 | goto end 170 | ) 171 | 172 | if "%1" == "linkcheck" ( 173 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 174 | if errorlevel 1 exit /b 1 175 | echo. 176 | echo.Link check complete; look for any errors in the above output ^ 177 | or in %BUILDDIR%/linkcheck/output.txt. 178 | goto end 179 | ) 180 | 181 | if "%1" == "doctest" ( 182 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 183 | if errorlevel 1 exit /b 1 184 | echo. 185 | echo.Testing of doctests in the sources finished, look at the ^ 186 | results in %BUILDDIR%/doctest/output.txt. 187 | goto end 188 | ) 189 | 190 | :end 191 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 16 | 17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 18 | 19 | help: 20 | @echo "Please use \`make ' where is one of" 21 | @echo " html to make standalone HTML files" 22 | @echo " dirhtml to make HTML files named index.html in directories" 23 | @echo " singlehtml to make a single large HTML file" 24 | @echo " pickle to make pickle files" 25 | @echo " json to make JSON files" 26 | @echo " htmlhelp to make HTML files and a HTML help project" 27 | @echo " qthelp to make HTML files and a qthelp project" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 31 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 32 | @echo " text to make text files" 33 | @echo " man to make manual pages" 34 | @echo " texinfo to make Texinfo files" 35 | @echo " info to make Texinfo files and run them through makeinfo" 36 | @echo " gettext to make PO message catalogs" 37 | @echo " changes to make an overview of all changed/added/deprecated items" 38 | @echo " linkcheck to check all external links for integrity" 39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 40 | 41 | clean: 42 | -rm -rf $(BUILDDIR)/* 43 | 44 | html: 45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 48 | 49 | dirhtml: 50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 51 | @echo 52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 53 | 54 | singlehtml: 55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 56 | @echo 57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 58 | 59 | pickle: 60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 61 | @echo 62 | @echo "Build finished; now you can process the pickle files." 63 | 64 | json: 65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 66 | @echo 67 | @echo "Build finished; now you can process the JSON files." 68 | 69 | htmlhelp: 70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 71 | @echo 72 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 73 | ".hhp project file in $(BUILDDIR)/htmlhelp." 74 | 75 | qthelp: 76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 77 | @echo 78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/dmrg101.qhcp" 81 | @echo "To view the help file:" 82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/dmrg101.qhc" 83 | 84 | devhelp: 85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 86 | @echo 87 | @echo "Build finished." 88 | @echo "To view the help file:" 89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/dmrg101" 90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/dmrg101" 91 | @echo "# devhelp" 92 | 93 | epub: 94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 95 | @echo 96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 97 | 98 | latex: 99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 100 | @echo 101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 103 | "(use \`make latexpdf' here to do that automatically)." 104 | 105 | latexpdf: 106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 107 | @echo "Running LaTeX files through pdflatex..." 108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 110 | 111 | text: 112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 113 | @echo 114 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 115 | 116 | man: 117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 118 | @echo 119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 120 | 121 | texinfo: 122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 123 | @echo 124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 125 | @echo "Run \`make' in that directory to run these through makeinfo" \ 126 | "(use \`make info' here to do that automatically)." 127 | 128 | info: 129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 130 | @echo "Running Texinfo files through makeinfo..." 131 | make -C $(BUILDDIR)/texinfo info 132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 133 | 134 | gettext: 135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 136 | @echo 137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 138 | 139 | changes: 140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 141 | @echo 142 | @echo "The overview file is in $(BUILDDIR)/changes." 143 | 144 | linkcheck: 145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 146 | @echo 147 | @echo "Link check complete; look for any errors in the above output " \ 148 | "or in $(BUILDDIR)/linkcheck/output.txt." 149 | 150 | doctest: 151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 152 | @echo "Testing of doctests in the sources finished, look at the " \ 153 | "results in $(BUILDDIR)/doctest/output.txt." 154 | -------------------------------------------------------------------------------- /dmrg101/core/reduced_DM.py: -------------------------------------------------------------------------------- 1 | # 2 | # File: diagonalize.py 3 | # Author: Ivan Gonzalez 4 | # 5 | """Diagonalizes a hermitian/square matrix using numpy 6 | """ 7 | import numpy as np 8 | from dmrg_exceptions import DMRGException 9 | 10 | def diagonalize(reduced_density_matrix): 11 | """Diagonalizes a hermitian or symmetric matrix. 12 | 13 | You use this function to diagonalize the reduced density matrix. 14 | It just calls the corresponding routine in numpy. 15 | 16 | Parameters 17 | ---------- 18 | reduced_density_matrix : a numpy array with ndim = 2 (a matrix) 19 | Should be hermitian (if complex), or symmetric (if real). 20 | 21 | Returns 22 | ------- 23 | eigenvals : a numpy array with ndim = 1. 24 | The eigenvalues (not ordered) of the reduced density matrix. 25 | The number of eigenvalues is the size if the matrix. 26 | eigenvecs : a numpy array with ndim = 1. 27 | The corresponding eigenvectors. Each column correspond to an 28 | eigenvector, such as eigenvecs[ : i] corresponds to 29 | eigenvals[i]. 30 | 31 | Raises 32 | ------ 33 | DMRGException 34 | if the computation cannot be performed. 35 | 36 | Examples 37 | -------- 38 | >>> import numpy as np 39 | >>> from dmrg101.core.reduced_DM import diagonalize 40 | >>> symmetric_matrix = np.array([[0.8, 0.5], 41 | ... [0.5, -0.25]]) 42 | >>> evals, evecs = diagonalize(symmetric_matrix) 43 | >>> print evals 44 | [-0.45 1. ] 45 | >>> tmp = np.dot(symmetric_matrix, evecs) 46 | >>> evecs_diagonalize_matrix = np.dot(evecs.transpose(), tmp) 47 | >>> print evecs_diagonalize_matrix 48 | [[ -0.45 0. ] 49 | [ 0. 1. ]] 50 | >>> hermitian_matrix = np.array([[0.0, -1.0j], 51 | ... [1.0j, 0.0]], dtype=complex) 52 | >>> evals, evecs = diagonalize(hermitian_matrix) 53 | >>> print evals 54 | [-1. 1.] 55 | >>> tmp = np.dot(hermitian_matrix, evecs) 56 | >>> evecs_diagonalize_matrix = np.dot(np.conj(evecs.transpose()), tmp) 57 | >>> print evecs_diagonalize_matrix 58 | [[ -1. 0. ] 59 | [ 0. 1. ]] 60 | """ 61 | try: 62 | eigenvals, eigenvecs = np.linalg.eigh(reduced_density_matrix) 63 | except LinAlgError: 64 | raise DMRGException("Error diagonalizing the reduced DM") 65 | 66 | return (eigenvals, eigenvecs) 67 | 68 | def truncate(reduced_density_matrix_eigenvals, 69 | reduced_density_matrix_eigenvecs, 70 | number_of_states_to_keep): 71 | """Truncates both the set of eigenvalues and eigenvectors. 72 | 73 | You use this function to truncate the set of eigenvalues and 74 | eigenvectors of the reduced density matrix to keep only the ones 75 | with larger eigenvalue. The number of eigenvalues kept is 76 | number_of_states_to_keep. 77 | 78 | If the number_of_states_to_keep is larger than the number of 79 | eigenvalues, all the eigenvalues are kept. 80 | 81 | The order of the columns in the matrix with the eigenvectors is 82 | not changed, i.e. if in the reduced_density_matrix passed as 83 | argument, the eigenvectors for the i-th and j-th eigenvalues 84 | satisfied i < j and both are kept, then i_p < j_p, where i_p, j_p 85 | are the column indexes that these eigenvectors occupy in the 86 | the resulting truncated matrix. 87 | 88 | Parameters 89 | ---------- 90 | reduced_density_matrix_eigenvals : a numpy array with ndim = 1 91 | The eigenvalues of the reduced density matrix (not need to be 92 | ordered). 93 | reduced_density_matrix_eigenvecs : a numpy array with ndim = 2 94 | The eigenvectors of the reduced density matrix. 95 | number_of_states_to_keep : an int 96 | The number of eigenvalues (or eigenvectors) kept. It is the same 97 | as the dimension of the truncated Hilbert space. 98 | 99 | Returns 100 | ------- 101 | truncated_eigenvals : a numpy array with ndim = 1. 102 | The eigenvalues kept in the same order as before. 103 | transformation_matrix : a numpy array with ndim = 2. 104 | The eigenvectors kept. this defines the DMRG transformation (a.k.a. 105 | truncation) matrix. 106 | 107 | Raises 108 | ------ 109 | DMRGException 110 | if the eigenvalues are not a 1-dim array, or the matrix with the 111 | eigenvecs is not square and with the proper dimensions. 112 | 113 | Examples 114 | -------- 115 | >>> import numpy as np 116 | >>> from dmrg101.core.reduced_DM import diagonalize, truncate 117 | >>> already_diag = np.array([[ 2.0, 0.0, 0.0 ], 118 | ... [ 0.0, 1.0, 0.0 ], 119 | ... [ 0.0, 0.0, 3.0 ]]) 120 | >>> evals, evecs = diagonalize(already_diag) 121 | >>> truncated_evals, truncation_matrix = truncate(evals, evecs, 2) 122 | >>> print truncated_evals 123 | [ 2. 3.] 124 | >>> print truncation_matrix 125 | [[ 1. 0.] 126 | [ 0. 0.] 127 | [ 0. 1.]] 128 | """ 129 | if reduced_density_matrix_eigenvals.ndim != 1: 130 | raise DMRGException("Bad arg: reduced_density_matrix_eigenvals") 131 | 132 | number_of_states = reduced_density_matrix_eigenvals.size 133 | 134 | if reduced_density_matrix_eigenvecs.shape != (number_of_states, 135 | number_of_states): 136 | raise DMRGException("Bad arg: reduced_density_matrix_eigenvecs") 137 | 138 | # if you don't have enough states, keep them all 139 | if (number_of_states_to_keep > number_of_states): 140 | number_of_states_to_keep = number_of_states 141 | # 142 | # sort the *indexes* of the eigenvals array (not the eigenvals themselves) 143 | # according to their eigenval in increasing order 144 | # 145 | indexes_in_increasing_order = np.argsort(reduced_density_matrix_eigenvals) 146 | # 147 | # get last number_of_states_to_keep of these indexes, i.e. the ones that 148 | # correspond to the largest number_of_states_to_keep eigenvals, and 149 | # reorder them back, so you don't change the original order of 150 | # the eigenvalues and eigenvectors. 151 | # 152 | indexes_to_keep = np.sort( 153 | indexes_in_increasing_order[-number_of_states_to_keep:]) 154 | # 155 | # numpy arrays support fancy indexing, such that given a subset of 156 | # the indexes to the original array gives a view of the array with 157 | # only the selected indexes. 158 | # 159 | truncated_eigenvals = reduced_density_matrix_eigenvals[indexes_to_keep] 160 | # 161 | # Making a (deep) copy is better, as then the resulting arrays are 162 | # contiguous in memory, i.e. pickup extra optimizations when 163 | # used in numerical calculations. 164 | # 165 | transformation_matrix = np.copy( 166 | reduced_density_matrix_eigenvecs[:, indexes_to_keep]) 167 | # 168 | # a few checks 169 | # 170 | assert(truncated_eigenvals.size == number_of_states_to_keep) 171 | assert(transformation_matrix.shape == (number_of_states, 172 | number_of_states_to_keep)) 173 | return (truncated_eigenvals, transformation_matrix) 174 | -------------------------------------------------------------------------------- /dmrg101/core/wavefunction.py: -------------------------------------------------------------------------------- 1 | # 2 | # File: wavefunction.py 3 | # Author: Ivan Gonzalez 4 | # 5 | """ A module for the wavefunctions. 6 | """ 7 | from math import sqrt 8 | import numpy as np 9 | from dmrg_exceptions import DMRGException 10 | from braket import braket 11 | 12 | def create_empty_like(wf): 13 | """Creates an new wavefunction empty but like the argument. 14 | 15 | You use this function to create wavefunctions with the same shape 16 | (i.e. in the same Hilbert space) as a given wavefunction, but with 17 | elements full of garbage. 18 | 19 | Parameters 20 | ---------- 21 | wf : a Wavefunction. 22 | The wavefunction you want to 'empty_like'. 23 | 24 | Returns 25 | ------- 26 | result : a Wavefunction. 27 | A new wavefunction with the same shape, but full of garbage. 28 | 29 | Examples 30 | -------- 31 | >>> import numpy as np 32 | >>> from dmrg101.core.wavefunction import create_empty_like 33 | >>> wf = Wavefunction(2, 2) 34 | >>> wf.as_matrix = np.eye(2, 2) 35 | >>> empty_like_wf = create_empty_like(wf) 36 | >>> print (wf.as_matrix.shape == empty_like_wf.as_matrix.shape) 37 | True 38 | """ 39 | result = Wavefunction(wf.left_dim, wf.right_dim, wf.num_type) 40 | result.as_matrix = np.empty_like(wf.as_matrix) 41 | return result 42 | 43 | class Wavefunction(object): 44 | """A wavefunction object 45 | 46 | You use this class to represent wavefunctions. Wavefunctions are 47 | stored as matrices, the rows corresponding to the states of the 48 | left block, and the columns corresponding to the states of the 49 | right block. 50 | 51 | """ 52 | def __init__(self, left_dim, right_dim, num_type='double'): 53 | """Creates an empty wavefunction 54 | 55 | The wavefunction has the correct dimensions, but their 56 | contents are garbage. You *must* give it a value before use it for 57 | any calculation. 58 | 59 | Parameters 60 | ---------- 61 | left_dim : an int 62 | The dimension of the Hilbert space of the left block 63 | right_dim : an int 64 | The dimension of the Hilbert space of the right block 65 | num_type : a double or complex 66 | The type of the wavefunction matrix elements. 67 | 68 | Raises 69 | ------ 70 | DMRGException 71 | if the left_dim, right_dim are not integers or bad args. 72 | """ 73 | super(Wavefunction, self).__init__() 74 | try: 75 | self.as_matrix = np.empty((left_dim, right_dim), 76 | num_type) 77 | except TypeError: 78 | raise DMRGException("Bad args for wavefunction") 79 | 80 | self.left_dim = left_dim 81 | self.right_dim = right_dim 82 | self.num_type = num_type 83 | 84 | def build_reduced_density_matrix(self, block_to_be_traced_over): 85 | """Constructs the reduced DM for this wavefunction. 86 | 87 | You use this function to build the reduced density matrix of this 88 | wavefunction. The reduced DM is itself a square and hermitian 89 | matrix as it should. 90 | 91 | Parameters 92 | ---------- 93 | block_to_be_traced_over : a string 94 | Which block (left or right) will be traced over. 95 | 96 | Returns 97 | ------- 98 | result : a numpy array with ndim = 2 99 | Which is an hermitian matrix with the reduced DM. 100 | 101 | Raises 102 | ------ 103 | DMRGException 104 | if the name for the block to be traced out is not correct. 105 | 106 | Examples 107 | -------- 108 | >>> import numpy as np 109 | >>> from dmrg101.core.wavefunction import Wavefunction 110 | >>> # a wf for a system with one state in the left Hilbert space 111 | >>> # (number of rows), and two states in the right Hilbert space 112 | >>> # (number of columns): just for demostration purposes. 113 | >>> wf = Wavefunction(1, 2) 114 | >>> wf.as_matrix = np.array([[1. ], [2. ]]) 115 | >>> print wf.as_matrix 116 | [[ 1.] 117 | [ 2.]] 118 | >>> # note that it's normalized, which is wrong, but 119 | >>> # allows to keep track of what's going on. 120 | >>> reduced_DM_for_left = wf.build_reduced_density_matrix('right') 121 | >>> print reduced_DM_for_left 122 | [[ 1. 2.] 123 | [ 2. 4.]] 124 | >>> # i.e. we traced out the left block, so it's a 2x2 matrix. 125 | >>> reduced_DM_for_right = wf.build_reduced_density_matrix('left') 126 | >>> print reduced_DM_for_right 127 | [[ 5.]] 128 | >>> # i.e. we traced out the right block, so it's a 1x1 matrix. 129 | """ 130 | if block_to_be_traced_over not in ('left', 'right'): 131 | raise DMRGException("block_to_be_traced_over must be left " 132 | "or right") 133 | 134 | result=np.array(self.as_matrix.dtype.name) 135 | 136 | if block_to_be_traced_over == 'left': 137 | result = np.dot(np.transpose(self.as_matrix), self.as_matrix) 138 | else: 139 | result = np.dot(self.as_matrix, np.transpose(self.as_matrix)) 140 | return result 141 | 142 | def get_norm(self): 143 | """ Calculates the norm of a wavefunction 144 | 145 | Simply uses the braket function to calculate the norm. 146 | The wavefunction is *unchanged* upon calculation. Use normalize if 147 | you want to normalize the wavefunction. 148 | 149 | Returns 150 | ------- 151 | result : a double 152 | The norm of the wavefunction 153 | 154 | See Also 155 | -------- 156 | normalize 157 | """ 158 | norm_squared = braket(self, self) 159 | 160 | # get rid of the complex part, which should be 0.0, for complex wfs 161 | if np.iscomplexobj(self.as_matrix): 162 | norm_squared = double(norm_squared.real) 163 | 164 | result = sqrt(norm_squared) 165 | return result 166 | 167 | def normalize(self): 168 | """ Normalizes the wavefunction 169 | 170 | Raises 171 | ------ 172 | DMRGException 173 | if the norm of the wavefunction is zero. 174 | 175 | Notes 176 | ----- 177 | Postcond : The wavefunction is normalized, i.e. changes to have norm 1.0. 178 | 179 | Examples 180 | -------- 181 | >>> import numpy as np 182 | >>> from dmrg101.core.wavefunction import Wavefunction 183 | >>> wf = Wavefunction(2, 2) 184 | >>> wf.as_matrix = np.ones((2, 2)) 185 | >>> print wf.as_matrix 186 | [[ 1. 1.] 187 | [ 1. 1.]] 188 | >>> wf.normalize() 189 | >>> print wf.as_matrix 190 | [[ 0.5 0.5] 191 | [ 0.5 0.5]] 192 | >>> norm = wf.get_norm() 193 | >>> print norm 194 | 1.0 195 | """ 196 | try: 197 | self.as_matrix /= self.get_norm() 198 | except: 199 | raise DMRGException("Wavefunction norm is zero") 200 | 201 | def randomize(self): 202 | """Fills the wavefunction with random values and normalizes. 203 | 204 | You use this function to generate a random wavefunction, i.e. one 205 | whose elements are random number. The wavefunction is normalized. 206 | The old elements of the wavefunction (if there were any) are 207 | lost after using this function. 208 | 209 | Notes 210 | ----- 211 | Postcond : The wavefunction is filled with random elements and has norm 1.0. 212 | 213 | Examples 214 | -------- 215 | >>> from dmrg101.core.wavefunction import Wavefunction 216 | >>> wf = Wavefunction(2, 2) 217 | >>> wf.randomize() 218 | >>> print wf.as_matrix # doctest: +ELLIPSIS 219 | [[... 220 | >>> norm = wf.get_norm() 221 | >>> print norm 222 | 1.0 223 | """ 224 | self.as_matrix = 2 * np.random.rand(self.left_dim, self.right_dim) - 1 225 | self.normalize() 226 | 227 | def set_to_zero(self): 228 | """Fills the wavefunction with all zeros. 229 | 230 | You use this function as a helper in cases when you need a 231 | wavefunction just full of zeros. 232 | 233 | Examples 234 | -------- 235 | >>> from dmrg101.core.wavefunction import Wavefunction 236 | >>> wf = Wavefunction(2, 2) 237 | >>> wf.set_to_zero() 238 | >>> print wf.as_matrix 239 | [[ 0. 0.] 240 | [ 0. 0.]] 241 | """ 242 | self.as_matrix = np.zeros((self.left_dim, self.right_dim)) 243 | -------------------------------------------------------------------------------- /dmrg101/core/operators.py: -------------------------------------------------------------------------------- 1 | # 2 | # File: operators.py 3 | # Author: Ivan Gonzalez 4 | # 5 | """The classes for quantum mechanical operators. 6 | """ 7 | import numpy as np 8 | from dmrg101.core.dmrg_exceptions import DMRGException 9 | from dmrg101.core.wavefunction import Wavefunction 10 | 11 | class OperatorComponent(object): 12 | """Abstract class for the composite pattern. 13 | 14 | This is an abstract class that just serves as a base for the 15 | implementation of the composite pattern. 16 | """ 17 | def __init__(self, left_dim, right_dim): 18 | super(object, self).__init__() 19 | self.left_dim = left_dim 20 | self.right_dim = right_dim 21 | 22 | def apply(self, wf): 23 | """Abstract method to apply the operator 24 | 25 | Does nothing actually. 26 | 27 | Parameters 28 | ---------- 29 | wf : a Wavefunction 30 | The wavefunction you want to apply the operator. 31 | """ 32 | pass 33 | 34 | class Operator(OperatorComponent): 35 | """A class for operators. 36 | 37 | This is the leaf class for the implementation of the composite 38 | pattern. 39 | 40 | You use this class when you want to build an operator that is not part 41 | of a linear combination of operators. 42 | 43 | Examples 44 | -------- 45 | >>> import numpy as np 46 | >>> from dmrg101.core.operators import Operator 47 | >>> # let's define the Pauli matrices 48 | >>> s_z = np.array([[-0.5, 0.0], 49 | ... [0.0, 0.5]]) 50 | >>> # hamiltonian of the Ising model for two spins 1/2 51 | >>> ising = Operator(s_z, s_z) 52 | >>> print ising.left_dim 53 | 2 54 | >>> print ising.right_dim 55 | 2 56 | >>> print ising.left_op 57 | [[-0.5 0. ] 58 | [ 0. 0.5]] 59 | >>> print ising.right_op 60 | [[-0.5 0. ] 61 | [ 0. 0.5]] 62 | >>> print ising.parameter 63 | 1.0 64 | """ 65 | def __init__(self, left_op, right_op, parameter=1.0): 66 | """Initalizes the operator. 67 | 68 | Checks the operator you are passing are square matrices. 69 | 70 | Parameters 71 | ---------- 72 | left_op : a numpy array of ndim = 2. 73 | The operator acting on the left indexes of the wavefunction. 74 | right_op : a numpy array of ndim = 2. 75 | The operator acting on the right indexes of the wavefunction. 76 | parameter : a double/complex, optional 77 | A parameter that multiplies the whole thing. 78 | 79 | Raises 80 | ------ 81 | DMRGException 82 | if any of the matrices you pass are not square. 83 | """ 84 | super(OperatorComponent, self).__init__() 85 | if left_op.shape[1] != left_op.shape[0]: 86 | raise DMRGException("Left operator is not a square matrix") 87 | if right_op.shape[1] != right_op.shape[0]: 88 | raise DMRGException("Right operator is not a square matrix") 89 | self.left_dim = left_op.shape[0] 90 | self.right_dim = right_op.shape[0] 91 | self.left_op = left_op 92 | self.right_op = right_op 93 | self.parameter = parameter 94 | 95 | def apply(self, wf): 96 | """ 97 | Applies the operator to a wavefunction. 98 | 99 | Parameters 100 | ---------- 101 | wf : A Wavefunction 102 | The wavefunction you want to apply the operator. 103 | 104 | Returns 105 | ------- 106 | result : a Wavefunction 107 | The wavefunction resulting of the operation. It has the same 108 | shape (i.e. is in the same Hilbert space) as the one passed as 109 | argument. 110 | 111 | Raises 112 | ------ 113 | DMRGException 114 | if `wf` is has not the correct dimensions as a matrix. 115 | 116 | Examples 117 | -------- 118 | >>> import numpy as np 119 | >>> from dmrg101.core.operators import Operator 120 | >>> from dmrg101.core.wavefunction import Wavefunction 121 | >>> wf = Wavefunction(2, 2) 122 | >>> wf.randomize() 123 | >>> identity_operator = Operator(np.eye(2, 2), np.eye(2, 2)) 124 | >>> new_wf = identity_operator.apply(wf) 125 | >>> np.array_equal(new_wf.as_matrix, wf.as_matrix) 126 | True 127 | """ 128 | if wf.as_matrix.shape != ((self.left_dim, self.right_dim)): 129 | raise DMRGException("Wavefunction does not fit.") 130 | 131 | result = Wavefunction(self.left_dim, self.right_dim) 132 | 133 | tmp = np.dot(self.right_op, wf.as_matrix.transpose()) 134 | result.as_matrix = self.parameter * np.dot(self.left_op, tmp.transpose()) 135 | 136 | return result 137 | 138 | class CompositeOperator(OperatorComponent): 139 | """A class for composite operators. 140 | 141 | This is the composite class for the implementation of the composite 142 | pattern. 143 | 144 | You use this class when you want to build an operator that it is a 145 | linear combination of other operators. 146 | 147 | Examples 148 | -------- 149 | >>> import numpy as np 150 | >>> from dmrg101.core.operators import CompositeOperator 151 | >>> # let's define the Pauli matrices 152 | >>> s_z = np.array([[-0.5, 0.0], 153 | ... [0.0, 0.5]]) 154 | >>> s_x = np.array([[0.0, 1.0], 155 | ... [1.0, 0.0]]) 156 | >>> one = np.array([[1.0, 0.0], 157 | ... [0.0, 1.0]]) 158 | >>> # hamiltonian of the TFIM for two spins 1/2 159 | >>> ising_in_tf = CompositeOperator(2, 2) 160 | >>> ising_in_tf.add(s_z, s_z) 161 | >>> # this is the magnetic field 162 | >>> h = 0.5 163 | >>> ising_in_tf.add(one, s_x, h) 164 | >>> ising_in_tf.add(s_x, one, h) 165 | >>> print ising_in_tf.list_of_components # doctest: +ELLIPSIS 166 | [... 167 | 168 | """ 169 | def __init__(self, left_dim, right_dim): 170 | super(OperatorComponent, self).__init__() 171 | self.left_dim = left_dim 172 | self.right_dim = right_dim 173 | self.list_of_components = [] 174 | 175 | def add(self, left_op, right_op, parameter=1.0): 176 | """ 177 | Add an operator to the composite. 178 | 179 | Parameters 180 | ---------- 181 | left_op : a numpy array of ndim = 2. 182 | The operator acting on the left indexes of the wavefunction. 183 | right_op : a numpy array of ndim = 2. 184 | The operator acting on the right indexes of the wavefunction. 185 | parameter : a double/complex. 186 | A parameter that multiplies the whole thing. 187 | """ 188 | op = Operator(left_op, right_op, parameter) 189 | if op.left_dim != self.left_dim or op.right_dim != self.right_dim: 190 | raise DMRGException("Operator cannot be added to composite") 191 | self.list_of_components.append(op) 192 | 193 | def apply(self, wf): 194 | """ 195 | Applies the composite operator to a wavefunction. 196 | 197 | Applies each of the operator components that form the composite 198 | operator and sums up the results. 199 | 200 | Parameters 201 | ---------- 202 | wf : A Wavefunction 203 | The wavefunction you want to apply the operator. 204 | 205 | Returns 206 | ------- 207 | result : a Wavefunction 208 | The wavefunction resulting of the operation. It has the same 209 | shape (i.e. is in the same Hilbert space) as the one passed as 210 | argument. 211 | 212 | Raises 213 | ------ 214 | DMRGException 215 | if self.list_of_components is empty. 216 | 217 | Examples 218 | -------- 219 | >>> import numpy as np 220 | >>> from dmrg101.core.operators import CompositeOperator 221 | >>> from dmrg101.core.wavefunction import Wavefunction 222 | >>> wf = Wavefunction(2, 2) 223 | >>> wf.randomize() 224 | >>> identity_operator = CompositeOperator(2, 2) 225 | >>> identity_operator.add(np.eye(2, 2), np.eye(2, 2)) 226 | >>> new_wf = identity_operator.apply(wf) 227 | >>> np.array_equal(new_wf.as_matrix, wf.as_matrix) 228 | True 229 | """ 230 | if not self.list_of_components: 231 | raise DMRGException("Composite operator is empty.") 232 | 233 | result = Wavefunction(self.left_dim, self.right_dim) 234 | result.set_to_zero() 235 | 236 | for component in self.list_of_components: 237 | result.as_matrix += component.apply(wf).as_matrix 238 | return result 239 | -------------------------------------------------------------------------------- /docs/old_stuff.rst: -------------------------------------------------------------------------------- 1 | ==== 2 | FAQs 3 | ==== 4 | 5 | How can I install Python? 6 | ------------------------- 7 | 8 | If you don't have *any* version of Python installed, you can download one 9 | at the `Python's official website `. Be sure to 10 | download a 2.x (e.g. 2.7.3) version, and not a 3.x (e.g. 3.3.0) version. 11 | 12 | If you have already *any* version of Python installed (even if it's a 3.x 13 | version) **do not** install another. Instead follow the instructions on 14 | `What if I don't have the required version of Python?`. 15 | 16 | Which version of Python do I have installed? 17 | -------------------------------------------- 18 | 19 | If you have Python [#]_ installed, you can check which version you 20 | have by doing [#]_: :: 21 | 22 | $ python --version 23 | 24 | You need a supported version. Python has gone thru a major overhaul from 25 | versions 3.x and above, and some of the libraries we use for dmrg101 may 26 | not work fine in Python 3.x. If you don't have the required version, 27 | follow the instructions on `What if I don't have the required version of 28 | Python?`. 29 | 30 | Which versions of Python are supported? 31 | --------------------------------------- 32 | 33 | Any version larger than 2.4.x, and smaller that 3.x. 34 | 35 | .. warning:: This may change with the final release of the code. So better 36 | wait. 37 | 38 | What if I don't have the required version of Python? 39 | ---------------------------------------------------- 40 | 41 | .. note:: This applies if you have a brand-new machine that ships with Python 3.x. 42 | Otherwise everything will probably work, and you may not need to 43 | install pythonbrew_. 44 | 45 | Do nothing! Unless you know very well what are you doing, do **not** 46 | install a new version of the interpreter from scratch. Python is used in 47 | your computer for a whole bunch of things and upgrading by brute force 48 | will surely break something. Fortunately we can workaround this by 49 | installing pythonbrew_. 50 | 51 | pythonbrew_ is a python package that allows you to have isolated 52 | installations of python in your home directory. The installations within 53 | pythonbrew_ do not interfere with your system-wide Python installation, so 54 | you can install and mess up as much as you want there without having sode 55 | effects in other parts of your machine. 56 | 57 | To install pythonbrew_, do: :: 58 | 59 | $ curl -kL http://xrl.us/pythonbrewinstall | bash 60 | 61 | And then add this into your ~/.bashrc: :: 62 | 63 | [[ -s $HOME/.pythonbrew/etc/bashrc ]] && source 64 | $HOME/.pythonbrew/etc/bashrc 65 | 66 | Once you have pythonbrew_ installed, install a version of python 67 | supported by our code (say 2.7.2): :: 68 | 69 | $ pythonbrew install -force 2.7.2 70 | 71 | .. warning:: This may take several minutes. 72 | 73 | You can start using your new (and isolated) python installation by doing: :: 74 | 75 | $ pythonbrew switch 2.7.2 76 | # Switched to Python-2.7.2 77 | $ python --version 78 | 2.7.2 79 | 80 | As was said above while you are using a *pythonbrewed* python, all the 81 | action happens inside a python installation in your home directory, 82 | instead of your system-wide python installation. To resume normal behavior 83 | and quit pythonbrew_ do: 84 | 85 | $ pythonbrew off 86 | 87 | You can switch back to your pythonbrew installed python versions by using 88 | the `switch` command again. 89 | 90 | What is virtualenv? 91 | ------------------- 92 | 93 | .. note:: pythonbrew_ has already virtualenv_ inside it. So if you are 94 | using pythonbrew you don't need to install virtualenv anew. 95 | 96 | virtualenv is a python package that allows you to isolate a Python 97 | distribution in a given directory of your hard drive, a virtual 98 | enviroment. Once this virtual enviroment is activated, you can install all 99 | kinds of python packages and even a different version of the python 100 | interpreter itself, without interfering with your system-wide python 101 | distribution. Once you're done using your virtual enviroment you can 102 | simply delete it. Outside your virtual enviroment your computer will be 103 | using your system-wide python distribution, so other processes that may be 104 | using python are not affected by the presence of your virtual enviroment. 105 | 106 | 107 | How to install virtualenv? 108 | -------------------------- 109 | 110 | .. note:: pythonbrew_ has already virtualenv_ inside it. So if you are 111 | using pythonbrew you don't need to install virtualenv anew. 112 | 113 | There are three ways, depending on which and whether you have installed a 114 | python package manager. Try them in order until one works. 115 | 116 | #. Using pip: :: 117 | 118 | $ pip install virtualenv 119 | 120 | #. Using easy_install: :: 121 | 122 | $ easy_install virtualenv 123 | 124 | #. `Grab the virtualenv.py file `_ and use it as: :: 125 | 126 | $ python virtualenv.py 127 | 128 | For choices 1) and 2) and depending the permission settings in your 129 | computer, you may need to prepend ``pip`` or ``easy_install`` commands above with ``sudo``, like: :: 130 | 131 | $ sudo easy_install virtualenv 132 | 133 | The first two will install virtualenv in your system. (This is always good 134 | ito have if you plan to use Python a lot.) The third option does not 135 | install anything, which may be also an option. 136 | 137 | For further information visit the `virtualenv's webpage 138 | `_. 139 | 140 | Creating a virtual environment for drmg101 141 | ------------------------------------------ 142 | 143 | To avoid messing with your system-wide python installation, and even if 144 | you have an adequate version of the python interpreter, we **strongly 145 | recommend** to create a virtual enviroment to install dmrg101. 146 | 147 | .. warning:: If you are using pythonbrew_ you need to create the environment after 148 | switching. See `Creating a virtual enviroment inside pythonbrew`. 149 | 150 | dmrg101 needs to install a few python packages to run. You may have some 151 | already installed if you are a python fan, or you may want to install them 152 | system-wide if you get hooked by python simplicity, but you can do this 153 | later. To avoid any mess in your computer, create a virtual environment to 154 | use with dmrg101 (you only need to do this once): :: 155 | 156 | $ python virtualenv.py dmrg101 157 | New python executable in dmrg101/bin/python 158 | Installing setuptools............done. 159 | Installing pip...............done. 160 | 161 | Then you **need** to activate the dmrg101 enviroment, using the scripts 162 | inside your new drmg101 directory: :: 163 | 164 | $ cd dmrg101 165 | $ source bin/activate 166 | 167 | You will notice that your prompt (the stuff appearing befoe the ``$`` in 168 | your console have changed and now starts by ``(dmrg101)``. This is a 169 | reminder that your are working inside the dmrg101 virtual environmment. 170 | 171 | .. warning:: From now on, unless stated otherwise, we assume that you are 172 | working inside your dmrg101 enviroment, that is you have created 173 | the enviroment dmrg101 and you have activated it. 174 | 175 | .. tip:: To let you know which commands you are supposed to be executed 176 | from inside the dmrg101 virtual enviroment, we will write the 177 | prompt as ``(dmrg101) $``, instead of the regular ``$``. 178 | 179 | When you are done using the dmrg101 code, you have to deactivate your 180 | enviroment: :: 181 | 182 | (dmrg101) $ deactivate 183 | 184 | This will make your prompt go back to normal and any call to Python will 185 | use your system-wide distribution. You can activate/deactivate the dmrg101 186 | environment again at any time as many times as you want. 187 | 188 | If you want to get rid of all the stuff that dmrg101 will install, just 189 | delete the dmrg101 folder. This will take back your system as it was 190 | before. 191 | 192 | ---------------------------------------------------------------------------- 193 | 194 | .. [#] We will use *Python* and *Python interpreter* to mean the same thing: 195 | what happens when you type ``python`` in a console. 196 | 197 | .. [#] Whenever you see code to type and the "$" symbol, means that you 198 | have to type this in a console in your computer. You can open a console 199 | in Linux and MacOS open the Terminal application, and in Windows it is 200 | called MS_DOS prompt or Windows command line. 201 | 202 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # dmrg101 documentation build configuration file, created by 4 | # sphinx-quickstart on Wed Oct 24 11:08:10 2012. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | sys.path.insert(0, os.path.abspath('..')) 20 | 21 | # -- General configuration ----------------------------------------------------- 22 | 23 | # If your documentation needs a minimal Sphinx version, state it here. 24 | #needs_sphinx = '1.0' 25 | 26 | # Add any Sphinx extension module names here, as strings. They can be extensions 27 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 28 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 29 | 'sphinx.ext.intersphinx', 'sphinx.ext.coverage', 'sphinx.ext.mathjax', 'numpydoc'] 30 | 31 | # Add any paths that contain templates here, relative to this directory. 32 | templates_path = ['_templates'] 33 | 34 | # The suffix of source filenames. 35 | source_suffix = '.rst' 36 | 37 | # The encoding of source files. 38 | #source_encoding = 'utf-8-sig' 39 | 40 | # The master toctree document. 41 | master_doc = 'index' 42 | 43 | # General information about the project. 44 | project = u'dmrg101' 45 | copyright = u'2012, Ivan Gonzalez' 46 | 47 | # The version info for the project you're documenting, acts as replacement for 48 | # |version| and |release|, also used in various other places throughout the 49 | # built documents. 50 | # 51 | # The short X.Y version. 52 | version = '0.1' 53 | # The full version, including alpha/beta/rc tags. 54 | release = '0.1' 55 | 56 | # The language for content autogenerated by Sphinx. Refer to documentation 57 | # for a list of supported languages. 58 | #language = None 59 | 60 | # There are two options for replacing |today|: either, you set today to some 61 | # non-false value, then it is used: 62 | #today = '' 63 | # Else, today_fmt is used as the format for a strftime call. 64 | #today_fmt = '%B %d, %Y' 65 | 66 | # List of patterns, relative to source directory, that match files and 67 | # directories to ignore when looking for source files. 68 | exclude_patterns = ['_build'] 69 | 70 | # The reST default role (used for this markup: `text`) to use for all documents. 71 | #default_role = None 72 | 73 | # If true, '()' will be appended to :func: etc. cross-reference text. 74 | #add_function_parentheses = True 75 | 76 | # If true, the current module name will be prepended to all description 77 | # unit titles (such as .. function::). 78 | #add_module_names = True 79 | 80 | # If true, sectionauthor and moduleauthor directives will be shown in the 81 | # output. They are ignored by default. 82 | #show_authors = False 83 | 84 | # The name of the Pygments (syntax highlighting) style to use. 85 | pygments_style = 'sphinx' 86 | 87 | # A list of ignored prefixes for module index sorting. 88 | #modindex_common_prefix = [] 89 | 90 | 91 | # -- Options for HTML output --------------------------------------------------- 92 | 93 | # The theme to use for HTML and HTML Help pages. See the documentation for 94 | # a list of builtin themes. 95 | html_theme = 'default' 96 | 97 | # Theme options are theme-specific and customize the look and feel of a theme 98 | # further. For a list of options available for each theme, see the 99 | # documentation. 100 | #html_theme_options = {} 101 | 102 | # Add any paths that contain custom themes here, relative to this directory. 103 | #html_theme_path = [] 104 | 105 | # The name for this set of Sphinx documents. If None, it defaults to 106 | # " v documentation". 107 | #html_title = None 108 | 109 | # A shorter title for the navigation bar. Default is the same as html_title. 110 | #html_short_title = None 111 | 112 | # The name of an image file (relative to this directory) to place at the top 113 | # of the sidebar. 114 | #html_logo = None 115 | 116 | # The name of an image file (within the static path) to use as favicon of the 117 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 118 | # pixels large. 119 | #html_favicon = None 120 | 121 | # Add any paths that contain custom static files (such as style sheets) here, 122 | # relative to this directory. They are copied after the builtin static files, 123 | # so a file named "default.css" will overwrite the builtin "default.css". 124 | html_static_path = ['_static'] 125 | 126 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 127 | # using the given strftime format. 128 | #html_last_updated_fmt = '%b %d, %Y' 129 | 130 | # If true, SmartyPants will be used to convert quotes and dashes to 131 | # typographically correct entities. 132 | #html_use_smartypants = True 133 | 134 | # Custom sidebar templates, maps document names to template names. 135 | #html_sidebars = {} 136 | 137 | # Additional templates that should be rendered to pages, maps page names to 138 | # template names. 139 | #html_additional_pages = {} 140 | 141 | # If false, no module index is generated. 142 | #html_domain_indices = True 143 | 144 | # If false, no index is generated. 145 | #html_use_index = True 146 | 147 | # If true, the index is split into individual pages for each letter. 148 | #html_split_index = False 149 | 150 | # If true, links to the reST sources are added to the pages. 151 | #html_show_sourcelink = True 152 | 153 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 154 | #html_show_sphinx = True 155 | 156 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 157 | #html_show_copyright = True 158 | 159 | # If true, an OpenSearch description file will be output, and all pages will 160 | # contain a tag referring to it. The value of this option must be the 161 | # base URL from which the finished HTML is served. 162 | #html_use_opensearch = '' 163 | 164 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 165 | #html_file_suffix = None 166 | 167 | # Output file base name for HTML help builder. 168 | htmlhelp_basename = 'dmrg101doc' 169 | 170 | 171 | # -- Options for LaTeX output -------------------------------------------------- 172 | 173 | latex_elements = { 174 | # The paper size ('letterpaper' or 'a4paper'). 175 | #'papersize': 'letterpaper', 176 | 177 | # The font size ('10pt', '11pt' or '12pt'). 178 | #'pointsize': '10pt', 179 | 180 | # Additional stuff for the LaTeX preamble. 181 | #'preamble': '', 182 | } 183 | 184 | # Grouping the document tree into LaTeX files. List of tuples 185 | # (source start file, target name, title, author, documentclass [howto/manual]). 186 | latex_documents = [ 187 | ('index', 'dmrg101.tex', u'dmrg101 Documentation', 188 | u'Ivan Gonzalez', 'manual'), 189 | ] 190 | 191 | # The name of an image file (relative to this directory) to place at the top of 192 | # the title page. 193 | #latex_logo = None 194 | 195 | # For "manual" documents, if this is true, then toplevel headings are parts, 196 | # not chapters. 197 | #latex_use_parts = False 198 | 199 | # If true, show page references after internal links. 200 | #latex_show_pagerefs = False 201 | 202 | # If true, show URL addresses after external links. 203 | #latex_show_urls = False 204 | 205 | # Documents to append as an appendix to all manuals. 206 | #latex_appendices = [] 207 | 208 | # If false, no module index is generated. 209 | #latex_domain_indices = True 210 | 211 | 212 | # -- Options for manual page output -------------------------------------------- 213 | 214 | # One entry per manual page. List of tuples 215 | # (source start file, name, description, authors, manual section). 216 | man_pages = [ 217 | ('index', 'dmrg101', u'dmrg101 Documentation', 218 | [u'Ivan Gonzalez'], 1) 219 | ] 220 | 221 | # If true, show URL addresses after external links. 222 | #man_show_urls = False 223 | 224 | 225 | # -- Options for Texinfo output ------------------------------------------------ 226 | 227 | # Grouping the document tree into Texinfo files. List of tuples 228 | # (source start file, target name, title, author, 229 | # dir menu entry, description, category) 230 | texinfo_documents = [ 231 | ('index', 'dmrg101', u'dmrg101 Documentation', 232 | u'Ivan Gonzalez', 'dmrg101', 'One line description of project.', 233 | 'Miscellaneous'), 234 | ] 235 | 236 | # Documents to append as an appendix to all manuals. 237 | #texinfo_appendices = [] 238 | 239 | # If false, no module index is generated. 240 | #texinfo_domain_indices = True 241 | 242 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 243 | #texinfo_show_urls = 'footnote' 244 | 245 | 246 | # Example configuration for intersphinx: refer to the Python standard library. 247 | intersphinx_mapping = {'http://docs.python.org/': None} 248 | -------------------------------------------------------------------------------- /dmrg101/core/sites.py: -------------------------------------------------------------------------------- 1 | # 2 | # File: sites.py 3 | # Author: Ivan Gonzalez 4 | # 5 | """ A module for single sites. 6 | """ 7 | import numpy as np 8 | from dmrg_exceptions import DMRGException 9 | 10 | class Site(object): 11 | """A general single site 12 | 13 | You use this class to create a single site. The site comes empty (i.e. 14 | with no operators included), but for th identity operator. You should 15 | add operators you need to make you site up. 16 | 17 | Parameters 18 | ---------- 19 | dim : an int 20 | Size of the Hilbert space. The dimension must be at least 1. A site of 21 | dim = 1 represents the vaccum (or something strange like that, it's 22 | used for demo purposes mostly.) 23 | operators : a dictionary of string and numpy array (with ndim = 2). 24 | Operators for the site. 25 | 26 | Examples 27 | -------- 28 | >>> from dmrg101.core.sites import Site 29 | >>> brand_new_site = Site(2) 30 | >>> # the Hilbert space has dimension 2 31 | >>> print brand_new_site.dim 32 | 2 33 | >>> # the only operator is the identity 34 | >>> print brand_new_site.operators 35 | {'id': array([[ 1., 0.], 36 | [ 0., 1.]])} 37 | """ 38 | def __init__(self, dim): 39 | """Creates an empty site of dimension dim. 40 | 41 | Raises 42 | ------ 43 | DMRGException 44 | if `dim` < 1. 45 | 46 | Notes 47 | ----- 48 | Postcond : The identity operator (ones in the diagonal, zeros elsewhere) 49 | is added to the `self.operators` dictionary. 50 | """ 51 | if dim < 1: 52 | raise DMRGException("Site dim must be at least 1") 53 | super(Site, self).__init__() 54 | self.dim = dim 55 | self.operators = { "id" : np.eye(self.dim, self.dim) } 56 | 57 | def add_operator(self, operator_name): 58 | """Adds an operator to the site. 59 | 60 | Parameters 61 | ---------- 62 | operator_name : string 63 | The operator name. 64 | 65 | Raises 66 | ------ 67 | DMRGException 68 | if `operator_name` is already in the dict. 69 | 70 | Notes 71 | ----- 72 | Postcond: 73 | 74 | - `self.operators` has one item more, and 75 | - the newly created operator is a (`self.dim`, `self.dim`) 76 | matrix of full of zeros. 77 | 78 | Examples 79 | -------- 80 | >>> from dmrg101.core.sites import Site 81 | >>> new_site = Site(2) 82 | >>> print new_site.operators.keys() 83 | ['id'] 84 | >>> new_site.add_operator('s_z') 85 | >>> print new_site.operators.keys() 86 | ['s_z', 'id'] 87 | >>> # note that the newly created op has all zeros 88 | >>> print new_site.operators['s_z'] 89 | [[ 0. 0.] 90 | [ 0. 0.]] 91 | """ 92 | if str(operator_name) in self.operators.keys(): 93 | raise DMRGException("Operator name exists already") 94 | else: 95 | self.operators[str(operator_name)] = np.zeros((self.dim, self.dim)) 96 | 97 | class PauliSite(Site): 98 | """A site for spin 1/2 models. 99 | 100 | You use this site for models where the single sites are spin 101 | one-half sites. The Hilbert space is ordered such as the first state 102 | is the spin down, and the second state is the spin up. Therefore e.g. 103 | you have the following relation between operator matrix elements: 104 | 105 | .. math:: 106 | \langle \downarrow \left| A \\right|\uparrow \\rangle = A_{0,1} 107 | 108 | Notes 109 | ----- 110 | Postcond : The site has already built-in the spin operators for s_z, s_p, s_m. 111 | 112 | Examples 113 | -------- 114 | >>> from dmrg101.core.sites import PauliSite 115 | >>> pauli_site = PauliSite() 116 | >>> # check all it's what you expected 117 | >>> print pauli_site.dim 118 | 2 119 | >>> print pauli_site.operators.keys() 120 | ['s_p', 's_z', 's_m', 'id'] 121 | >>> print pauli_site.operators['s_z'] 122 | [[-1. 0.] 123 | [ 0. 1.]] 124 | >>> print pauli_site.operators['s_x'] 125 | [[ 0. 1.] 126 | [ 1. 0.]] 127 | """ 128 | def __init__(self): 129 | """Creates the spin one-half site with Pauli matrices. 130 | 131 | Notes 132 | ----- 133 | Postcond : the dimension is set to 2, and the Pauli matrices 134 | are added as operators. 135 | """ 136 | super(PauliSite, self).__init__(2) 137 | # add the operators 138 | self.add_operator("s_z") 139 | self.add_operator("s_x") 140 | # for clarity 141 | s_z = self.operators["s_z"] 142 | s_x = self.operators["s_x"] 143 | # set the matrix elements different from zero to the right values 144 | s_z[0, 0] = -1.0 145 | s_z[1, 1] = 1.0 146 | s_x[0, 1] = 1.0 147 | s_x[1, 0] = 1.0 148 | 149 | class SpinOneHalfSite(Site): 150 | """A site for spin 1/2 models. 151 | 152 | You use this site for models where the single sites are spin 153 | one-half sites. The Hilbert space is ordered such as the first state 154 | is the spin down, and the second state is the spin up. Therefore e.g. 155 | you have the following relation between operator matrix elements: 156 | 157 | .. math:: 158 | \langle \downarrow \left| A \\right|\uparrow \\rangle = A_{0,1} 159 | 160 | Notes 161 | ----- 162 | Postcond : The site has already built-in the spin operators for s_z, s_p, s_m. 163 | 164 | Examples 165 | -------- 166 | >>> from dmrg101.core.sites import SpinOneHalfSite 167 | >>> spin_one_half_site = SpinOneHalfSite() 168 | >>> # check all it's what you expected 169 | >>> print spin_one_half_site.dim 170 | 2 171 | >>> print spin_one_half_site.operators.keys() 172 | ['s_p', 's_z', 's_m', 'id'] 173 | >>> print spin_one_half_site.operators['s_z'] 174 | [[-0.5 0. ] 175 | [ 0. 0.5]] 176 | >>> print spin_one_half_site.operators['s_p'] 177 | [[ 0. 0.] 178 | [ 1. 0.]] 179 | >>> print spin_one_half_site.operators['s_m'] 180 | [[ 0. 1.] 181 | [ 0. 0.]] 182 | """ 183 | def __init__(self): 184 | """Creates the spin one-half site. 185 | 186 | Notes 187 | ----- 188 | Postcond : the dimension is set to 2, and the Pauli matrices 189 | are added as operators. 190 | """ 191 | super(SpinOneHalfSite, self).__init__(2) 192 | # add the operators 193 | self.add_operator("s_z") 194 | self.add_operator("s_p") 195 | self.add_operator("s_m") 196 | self.add_operator("s_x") 197 | # for clarity 198 | s_z = self.operators["s_z"] 199 | s_p = self.operators["s_p"] 200 | s_m = self.operators["s_m"] 201 | s_x = self.operators["s_x"] 202 | # set the matrix elements different from zero to the right values 203 | s_z[0, 0] = -0.5 204 | s_z[1, 1] = 0.5 205 | s_p[1, 0] = 1.0 206 | s_m[0, 1] = 1.0 207 | s_x[0, 1] = 0.5 208 | s_x[1, 0] = 0.5 209 | 210 | 211 | class ElectronicSite(Site): 212 | """A site for electronic models 213 | 214 | You use this site for models where the single sites are electron 215 | sites. The Hilbert space is ordered such as: 216 | 217 | - the first state, labelled 0, is the empty site, 218 | - the second, labelled 1, is spin down, 219 | - the third, labelled 2, is spin up, and 220 | - the fourth, labelled 3, is double occupancy. 221 | 222 | Notes 223 | ----- 224 | Postcond: The site has already built-in the spin operators for: 225 | 226 | - c_up : destroys an spin up electron, 227 | - c_up_dag, creates an spin up electron, 228 | - c_down, destroys an spin down electron, 229 | - c_down_dag, creates an spin down electron, 230 | - s_z, component z of spin, 231 | - s_p, raises the component z of spin, 232 | - s_m, lowers the component z of spin, 233 | - n_up, number of electrons with spin up, 234 | - n_down, number of electrons with spin down, 235 | - n, number of electrons, i.e. n_up+n_down, and 236 | - u, number of double occupancies, i.e. n_up*n_down. 237 | 238 | Examples 239 | -------- 240 | >>> from dmrg101.core.sites import ElectronicSite 241 | >>> hubbard_site = ElectronicSite() 242 | >>> # check all it's what you expected 243 | >>> print hubbard_site.dim 244 | 4 245 | >>> print hubbard_site.operators.keys() # doctest: +ELLIPSIS 246 | ['s_p', ...] 247 | >>> print hubbard_site.operators['n_down'] 248 | [[ 0. 0. 0. 0.] 249 | [ 0. 1. 0. 0.] 250 | [ 0. 0. 0. 0.] 251 | [ 0. 0. 0. 1.]] 252 | >>> print hubbard_site.operators['n_up'] 253 | [[ 0. 0. 0. 0.] 254 | [ 0. 0. 0. 0.] 255 | [ 0. 0. 1. 0.] 256 | [ 0. 0. 0. 1.]] 257 | >>> print hubbard_site.operators['u'] 258 | [[ 0. 0. 0. 0.] 259 | [ 0. 0. 0. 0.] 260 | [ 0. 0. 0. 0.] 261 | [ 0. 0. 0. 1.]] 262 | """ 263 | def __init__(self): 264 | super(ElectronicSite, self).__init__(4) 265 | # add the operators 266 | self.add_operator("c_up") 267 | self.add_operator("c_up_dag") 268 | self.add_operator("c_down") 269 | self.add_operator("c_down_dag") 270 | self.add_operator("s_z") 271 | self.add_operator("s_p") 272 | self.add_operator("s_m") 273 | self.add_operator("n_up") 274 | self.add_operator("n_down") 275 | self.add_operator("n") 276 | self.add_operator("u") 277 | # for clarity 278 | c_up = self.operators["c_up"] 279 | c_up_dag = self.operators["c_up_dag"] 280 | c_down = self.operators["c_down"] 281 | c_down_dag = self.operators["c_down_dag"] 282 | s_z = self.operators["s_z"] 283 | s_p = self.operators["s_p"] 284 | s_m = self.operators["s_m"] 285 | n_up = self.operators["n_up"] 286 | n_down = self.operators["n_down"] 287 | n = self.operators["n"] 288 | u = self.operators["u"] 289 | # set the matrix elements different from zero to the right values 290 | # TODO: missing s_p, s_m 291 | c_up[0,2] = 1.0 292 | c_up[1,3] = 1.0 293 | c_up_dag[2,0] = 1.0 294 | c_up_dag[3,1] = 1.0 295 | c_down[0,1] = 1.0 296 | c_down[2,3] = 1.0 297 | c_down_dag[1,0] = 1.0 298 | c_down_dag[3,2] = 1.0 299 | s_z[1,1] = -1.0 300 | s_z[2,2] = 1.0 301 | n_up[2,2] = 1.0 302 | n_up[3,3] = 1.0 303 | n_down[1,1] = 1.0 304 | n_down[3,3] = 1.0 305 | n[1,1] = 1.0 306 | n[2,2] = 1.0 307 | n[3,3] = 2.0 308 | u[3,3] = 1.0 309 | -------------------------------------------------------------------------------- /dmrg101/core/lanczos.py: -------------------------------------------------------------------------------- 1 | # 2 | # File: lanczos.py 3 | # Author: Ivan Gonzalez 4 | # 5 | """ Implements the Lanczos algorithm. 6 | 7 | Implements the Lanczos algorithm to calculate the ground state energy and 8 | wavefunction of a given Hamiltonian. The objects representing the 9 | wavefunction and hamiltonian are Wavefunction and OperatorComponent (i.e. 10 | an Operator or a CompositeOperator), respectively. 11 | 12 | Most of the methods here are just convenience functions. The one that 13 | implements the algorithm is :func:`calculate_ground_state`. 14 | 15 | Methods 16 | ------- 17 | 18 | - calculate_ground_state_energy(hamiltonian, initial_wf, 19 | min_lanczos_iterations, too_many_iterations, precision) : Calculates the 20 | ground state energy. 21 | - calculate_ground_state_wf(d, e, saved_lanczos_vectors) : Calculates the 22 | ground state wavefunction. 23 | - calculate_ground_state(hamiltonian, [initial_wf, min_lanczos_iterations, 24 | too_many_iterations, precision]): Calculates the ground state energy and 25 | wavefunction. 26 | - create_lanczos_vectors(initial_wf) : Creates the three Lanczos vectors. 27 | - cycle_lanczos_vectors(lv, saved_lanczos_vectors) : Cycles the Lanczos 28 | vectors to prepare them for the next iteration. 29 | - diagonalize_tridiagonal_matrix(d, e, eigenvectors) : Diagonalizes the 30 | tridiagonal matrix in the Lanczos. 31 | - improve_ground_state_energy(d, e, current_gs_energy, precision) : Gets 32 | an improved value for the ground state energy. 33 | - generate_tridiagonal_matrix(alpha, beta, iteration) : Generates the 34 | elements of the tridiagonal matrix. 35 | - lanczos_zeroth_iteration(alpha, beta, lv, hamiltonian) : Performs the 36 | zero-th iteration for the Lanczos. 37 | - lanczos_nth_iteration(alpha, beta, lv, saved_lanczos_vectors, 38 | hamiltonian, iteration) : Performs the n-th iteration for the Lanczos. 39 | 40 | Examples 41 | -------- 42 | >>> import numpy as np 43 | >>> from dmrg101.core.operators import CompositeOperator 44 | >>> from dmrg101.core.lanczos import calculate_ground_state 45 | >>> s_z = np.array([[-0.5, 0.0], 46 | ... [0.0, 0.5]]) 47 | >>> one = np.array([[1.0, 0.0], 48 | ... [0.0, 1.0]]) 49 | >>> ising_fm_in_field = CompositeOperator(2, 2) 50 | >>> ising_fm_in_field.add(s_z, s_z, -1.0) 51 | >>> h = 0.1 52 | >>> ising_fm_in_field.add(s_z, one, -h) 53 | >>> ising_fm_in_field.add(one, s_z, -h) 54 | >>> gs_energy, gs_wf = calculate_ground_state(ising_fm_in_field) 55 | >>> print gs_energy 56 | -0.35 57 | >>> print gs_wf.as_matrix 58 | [[ 0. 0.] 59 | [[ 0. 1.] 60 | 61 | ------------------------------------------------------ 62 | 63 | .. [1] http://en.wikipedia.org/wiki/Lanczos_algorithm 64 | """ 65 | import numpy as np 66 | from math import fabs 67 | from sys import float_info 68 | from dmrg101.core.braket import braket 69 | from dmrg101.core.dmrg_exceptions import DMRGException 70 | from dmrg101.core.get_real import get_real 71 | from dmrg101.core.wavefunction import create_empty_like 72 | from dmrg101.core.wavefunction import Wavefunction 73 | from dmrg101.utils.tridiagonal_solver.tridiagonal_solver import tridiagonal_solver 74 | 75 | def create_lanczos_vectors(initial_wf): 76 | """Creates the three Lanczos vectors. 77 | 78 | The Lanczos vectors are created empty, but with the proper size and 79 | type. The first lanczos vector is set to have the same eleements as 80 | the `initial_wf`. 81 | 82 | Parameters 83 | ---------- 84 | initial_wf : a Wavefunction. 85 | The initial wavefunction serving as seed in the Lanczos algorithm. 86 | 87 | Returns 88 | ------- 89 | result : a tuple of 3 Wavefunctions. 90 | The three Lanczos vectors. They have the same shape and type as 91 | initial_wf. The first has the same elements (is a copy), and the 92 | last two are full of garbage. 93 | """ 94 | result = [create_empty_like(initial_wf), 95 | create_empty_like(initial_wf), 96 | create_empty_like(initial_wf)] 97 | result[0].as_matrix = np.copy(initial_wf.as_matrix) 98 | return result 99 | 100 | def generate_tridiagonal_matrix(alpha, beta, iteration): 101 | """Generates the elements of the tridiagonal matrix. 102 | 103 | You use this function to reshuffle the alpha and beta to generate the 104 | diagonal and off-diagonal elements of the tridiagonal matrix. Note 105 | that `d`, `e` sizes depend on the `iteration`. 106 | 107 | The off-diagonal element array `e` have one less element, but you 108 | padded by the end with an extra 0.0 (so append a last element equal to 0.0). 109 | 110 | Parameters 111 | ---------- 112 | alpha : a list of doubles. 113 | The alpha's in the Lanczos algorithm. 114 | beta : a list of doubles. 115 | The beta's in the Lanczos algorithm. 116 | iteration : an int 117 | The iteration you are, which sets the size of d, e. 118 | 119 | Returns 120 | ------- 121 | d : a numpy array with ndim = 1. 122 | The elements of the diagonal of the tridiagonal matrix. The size 123 | of `d` is `iteration`+1. 124 | e : a numpy array with ndim = 1. 125 | The off-diagonal elements of the tridiagonal matrix. The size of 126 | `e` is `iteration`. 127 | 128 | Raises 129 | ------ 130 | DMRGException 131 | if `alpha` and `beta` have different sizes, or you are not in the 132 | second iteration at least. 133 | """ 134 | if (len(alpha) != iteration + 1): 135 | raise DMRGException("alpha has wrong size") 136 | if (len(alpha) != len(beta) ): 137 | raise DMRGException("beta has wrong size") 138 | if not (len(alpha) > 1): 139 | raise DMRGException("alpha not large enough") 140 | d = np.array(alpha) 141 | e = np.array(beta[:d.size-1]) 142 | assert(d.size == e.size + 1) 143 | assert(d.size == iteration+1) 144 | return d, e 145 | 146 | def diagonalize_tridiagonal_matrix(d, e, eigenvectors): 147 | """Diagonalizes the tridiagonal matrix in the Lanczos. 148 | 149 | Just a wrapper. 150 | 151 | Parameters 152 | ---------- 153 | d : a numpy array with ndim = 1. 154 | The elements of the diagonal of the tridiagonal matrix. 155 | e : a numpy array with ndim = 1. 156 | The off-diagonal elements of the tridiagonal matrix. 157 | eigenvectors : a bool 158 | Whether eigenvectors are calculated or not. 159 | 160 | Returns 161 | ------- 162 | evals : a numpy array with ndim = 1. 163 | The eigenvalues. 164 | evecs : a numpy array with ndim = 2. 165 | The eigenvectors. 166 | 167 | Raises 168 | ------ 169 | DMRGException 170 | if the arrays `d` and `e` have different sizes. 171 | """ 172 | if ( d.size != e.size + 1): 173 | raise DMRGException("Wrong sizes for d, e") 174 | evals, evecs = tridiagonal_solver(d, e, eigenvectors) 175 | return evals, evecs 176 | 177 | def lanczos_zeroth_iteration(alpha, beta, lv, hamiltonian): 178 | """Performs the zero-th iteration for the Lanczos. 179 | 180 | The zero-th (i.e. the first at all) Lanczos iteration is slightly 181 | different to the rest. 182 | 183 | Parameters 184 | ---------- 185 | alpha : a list of doubles. 186 | The alpha's in the Lanczos algorithm. 187 | beta : a list of doubles. 188 | The beta's in the Lanczos algorithm. 189 | lv : a 3-tuple of numpy arrays of ndim = 2. 190 | The Lanczos vectors. 191 | hamiltonian : a CompositeOperator 192 | The hamiltonian you want to diagonalize. 193 | 194 | Returns 195 | ------- 196 | already_the_ground_state : a bool 197 | Whether the zeroth iteration gives you the ground state. This 198 | happens when the initial wavefunction for the Lanczos is already 199 | the groudn state. 200 | 201 | Raises 202 | ------ 203 | DMRGException : 204 | if the `alpha` or `beta` lists are not empty. 205 | """ 206 | if alpha and beta: 207 | raise DMRGException("Lists not empty at zeroth Lanczos iter") 208 | lv[1] = hamiltonian.apply(lv[0]) 209 | alpha.append(get_real(braket(lv[0], lv[1]))) 210 | lv[1].as_matrix -= alpha[0]*lv[0].as_matrix 211 | beta.append(lv[1].get_norm()) 212 | lv[1].normalize() 213 | assert(len(alpha) == 1) 214 | assert(len(beta) == 1) 215 | already_the_ground_state = ( beta[0] < float_info.epsilon ) 216 | return already_the_ground_state 217 | 218 | def lanczos_nth_iteration(alpha, beta, lv, saved_lanczos_vectors, 219 | hamiltonian, iteration): 220 | """Performs the n-th iteration for the Lanczos. 221 | 222 | It calculates the new values for `alpha` and `beta` for this 223 | `iteration`. 224 | 225 | Parameters 226 | ---------- 227 | alpha : a list of doubles. 228 | The alpha's in the Lanczos algorithm. 229 | beta : a list of doubles. 230 | The beta's in the Lanczos algorithm. 231 | lv : the 3 tuple of Wavefunctions. 232 | With the three Lanczos vectors in use. 233 | saved_lanczos_vectors : a list of Wavefunctions. 234 | The Lanczos vectors that are saved. 235 | hamiltonian : a CompositeOperator 236 | The hamiltonian you want to diagonalize. 237 | iteration : an int 238 | The iteration number. 239 | 240 | Notes 241 | ----- 242 | Postcond : The 3rd Lanczos vector in the tuple is modified. The first 243 | two are *not*. 244 | 245 | Raises 246 | ------ 247 | DMRGException 248 | if `alpha.size` is not equal to `beta.size` 249 | """ 250 | if len(alpha) != len(beta): 251 | DMRGException("alpha and beta have wrong sizes") 252 | 253 | lv[2] = hamiltonian.apply(lv[1]) 254 | alpha.append(get_real(braket(lv[1], lv[2]))) 255 | lv[2].as_matrix -= (alpha[iteration]*lv[1].as_matrix + 256 | beta[iteration-1]*lv[0].as_matrix) 257 | beta.append(lv[2].get_norm()) 258 | lv[2].normalize() 259 | cycle_lanczos_vectors(lv, saved_lanczos_vectors) 260 | assert(len(alpha) == iteration + 1) 261 | assert(len(beta) == iteration + 1) 262 | 263 | def cycle_lanczos_vectors(lv, saved_lanczos_vectors): 264 | """Cycles the Lanczos vectors to prepare them for the next iteration. 265 | 266 | You use this function to cycle the Lanczos vectors in this way: 267 | 268 | - lv[1] -> lv[0] 269 | - lv[2] -> lv[1] 270 | 271 | The first Lanczos vector before the cycle, `lv[0]` is not needed 272 | anymore and is appended to the `saved_lanczos_vectors` list. The last 273 | Lanczos vector after the cycle, `lv[2]` contains garbage. 274 | 275 | Parameters 276 | ---------- 277 | lv : the 3 tuple of Wavefunctions. 278 | With the three Lanczos vectors in use. 279 | saved_lanczos_vectors : a list of Wavefunctions. 280 | The Lanczos vectors that are saved. 281 | """ 282 | saved_lanczos_vectors.append(lv[0]) 283 | lv[0], lv[1], lv[2] = lv[1], lv[2], create_empty_like(lv[2]) 284 | 285 | def improve_ground_state_energy(d, e, current_gs_energy, precision): 286 | """Gets an improved value for the ground state energy. 287 | 288 | Diagonalizes the tridiagonal matrix and find its lowest eigenvalues. 289 | Then checks whether this lowest eigenvalue improves the current 290 | ground state energy. 291 | 292 | Parameters 293 | ---------- 294 | d : a numpy array with ndim = 1. 295 | The elements of the diagonal of the tridiagonal matrix. 296 | e : a numpy array with ndim = 1. 297 | The off-diagonal elements of the tridiagonal matrix. 298 | current_gs_energy : a double 299 | The current (best) value for the ground state energy. You use here 300 | the value coming from the previous iteration of the Lanczos. 301 | precision : a double. 302 | The ratio to the current_gs_energy that is accepted as no 303 | improvement. 304 | 305 | Returns 306 | ------- 307 | does_not_improve_anymore : a bool. 308 | Whether the new value improves the current one or not. 309 | new_gs_energy : a double. 310 | The new value for the ground state energy. 311 | """ 312 | evals, evecs = diagonalize_tridiagonal_matrix(d, e, False) 313 | minimum_eval = np.amin(evals) 314 | if current_gs_energy is not None: 315 | difference_with_current = fabs(current_gs_energy - minimum_eval) 316 | acceptable_difference = precision * fabs(current_gs_energy) 317 | does_not_improve_anymore = (difference_with_current < acceptable_difference) 318 | else: 319 | does_not_improve_anymore = False 320 | new_gs_energy = minimum_eval 321 | return does_not_improve_anymore, new_gs_energy 322 | 323 | def calculate_ground_state_energy(hamiltonian, initial_wf, 324 | min_lanczos_iterations, 325 | too_many_iterations, 326 | precision): 327 | """Calculates the ground state energy. 328 | 329 | Parameters 330 | ---------- 331 | hamiltonian : a CompositeOperator. 332 | The hamiltonian you want to diagonalize. 333 | initial_wf : a Wavefunction. 334 | The wavefunction that will be used as seed. If None, a random one 335 | if used. 336 | min_lanczos_iterations : an int. 337 | The number of iterations before starting the diagonalizations. 338 | too_many_iterations : a int. 339 | The maximum number of iterations allowed. 340 | precision : a double. 341 | The accepted precision to which the ground state energy is 342 | considered not improving. 343 | 344 | Returns 345 | ------- 346 | gs_energy : a double. 347 | The ground state energy. 348 | d : a numpy array with ndim = 1. 349 | The elements of the diagonal of the tridiagonal matrix. 350 | e : a numpy array with ndim = 1. 351 | The off-diagonal elements of the tridiagonal matrix. 352 | 353 | Raises 354 | ------ 355 | DMRGException 356 | if the number of iterations goes over `too_many_iterations`. 357 | """ 358 | alpha = [] 359 | beta = [] 360 | lv = create_lanczos_vectors(initial_wf) 361 | saved_lanczos_vectors = [] 362 | 363 | iteration = 0 364 | is_initial_wf_the_gs = lanczos_zeroth_iteration(alpha, beta, lv, hamiltonian) 365 | gs_energy = None 366 | 367 | # check if the initial_wf is already the ground state 368 | if not is_initial_wf_the_gs: 369 | we_are_done = False 370 | while not we_are_done: 371 | iteration += 1 372 | if iteration >= too_many_iterations: 373 | raise DMRGException("Too many Lanczos iterations") 374 | 375 | lanczos_nth_iteration(alpha, beta, lv, saved_lanczos_vectors, 376 | hamiltonian, iteration) 377 | 378 | if iteration >= min_lanczos_iterations: 379 | d, e = generate_tridiagonal_matrix(alpha, beta, iteration) 380 | we_are_done, gs_energy = improve_ground_state_energy(d, e, 381 | gs_energy, precision) 382 | 383 | assert(we_are_done) 384 | saved_lanczos_vectors.append(lv[1]) 385 | #saved_lanczos_vectors.append(lv[2]) 386 | else: # initial_wf *is* the ground state 387 | gs_energy = alpha[0] 388 | 389 | assert(gs_energy is not None) 390 | return gs_energy, d, e, saved_lanczos_vectors 391 | 392 | def calculate_ground_state_wf(d, e, saved_lanczos_vectors): 393 | """Calculates the ground state wavefunction. 394 | 395 | Parameters 396 | ---------- 397 | d : a numpy array with ndim = 1. 398 | The elements of the diagonal of the tridiagonal matrix. The size 399 | of `d`. 400 | e : a numpy array with ndim = 1. 401 | The off-diagonal elements of the tridiagonal matrix. The size of 402 | `e` equals the size of `d`, and it is padded with a zero at the 403 | end. 404 | saved_lanczos_vectors : a list of Wavefunctions. 405 | The Lanczos vectors that are saved. 406 | 407 | Returns 408 | ------- 409 | result : a Wavefunction. 410 | The ground state function (normalized). 411 | """ 412 | evals, evecs = diagonalize_tridiagonal_matrix(d, e, True) 413 | min_index = np.argsort(evals)[0] 414 | coefficients_of_gs_in_krylov_space = evecs[:, min_index] 415 | assert(len(saved_lanczos_vectors) == len(coefficients_of_gs_in_krylov_space)) 416 | 417 | result = Wavefunction(saved_lanczos_vectors[0].left_dim, 418 | saved_lanczos_vectors[0].right_dim) 419 | result.set_to_zero() 420 | for i in range(len(saved_lanczos_vectors)): 421 | result.as_matrix += ( coefficients_of_gs_in_krylov_space[i] * 422 | saved_lanczos_vectors[i].as_matrix ) 423 | 424 | result.normalize() 425 | return result 426 | 427 | def calculate_ground_state(hamiltonian, initial_wf = None, 428 | min_lanczos_iterations = 3, 429 | too_many_iterations = 1000, 430 | precision = 0.000001): 431 | """Calculates the ground state energy and wavefunction. 432 | 433 | Parameters 434 | ---------- 435 | hamiltonian : a CompositeOperator 436 | The hamiltonian you want to diagonalize. 437 | initial_wf : a Wavefunction, optional 438 | The wavefunction that will be used as seed. If None, a random one 439 | if used. 440 | min_lanczos_iterations : an int, optional. 441 | The number of iterations before starting the diagonalizations. 442 | too_many_iterations : a int, optional. 443 | The maximum number of iterations allowed. 444 | precision : a double, optional. 445 | The accepted precision to which the ground state energy is 446 | considered not improving. 447 | 448 | Returns 449 | ------- 450 | gs_energy : a double. 451 | The ground state energy. 452 | gs_wf : a Wavefunction. 453 | The ground state wavefunction (normalized.) 454 | """ 455 | if initial_wf is None: 456 | initial_wf = Wavefunction(hamiltonian.left_dim, 457 | hamiltonian.right_dim) 458 | initial_wf.randomize() 459 | 460 | gs_energy, d, e, saved_lanczos_vectors = ( 461 | calculate_ground_state_energy(hamiltonian, initial_wf, min_lanczos_iterations, 462 | too_many_iterations, precision) ) 463 | gs_wf = calculate_ground_state_wf(d, e, saved_lanczos_vectors) 464 | 465 | return gs_energy, gs_wf 466 | -------------------------------------------------------------------------------- /dmrg101/core/system.py: -------------------------------------------------------------------------------- 1 | # 2 | # File: block.py 3 | # Author: Ivan Gonzalez 4 | # 5 | """ A module for a DMRG system. 6 | """ 7 | import numpy as np 8 | from copy import deepcopy 9 | from block import make_block_from_site, Block 10 | from dmrg_exceptions import DMRGException 11 | import lanczos 12 | from make_tensor import make_tensor 13 | from operators import CompositeOperator 14 | from transform_matrix import transform_matrix 15 | from entropies import calculate_entropy, calculate_renyi 16 | from reduced_DM import diagonalize, truncate 17 | from truncation_error import calculate_truncation_error 18 | 19 | def make_updated_block_for_site(transformation_matrix, 20 | operators_to_add_to_block): 21 | """Make a new block for a list of operators. 22 | 23 | Takes a dictionary of operator names and matrices and makes a new 24 | block inserting in the `operators` block dictionary the result of 25 | transforming the matrices in the original dictionary accoring to the 26 | transformation matrix. 27 | 28 | You use this function everytime you want to create a new block by 29 | transforming the current operators to a truncated basis. 30 | 31 | Parameters 32 | ---------- 33 | transformation_matrix : a numpy array of ndim = 2. 34 | The transformation matrix coming from a (truncated) unitary 35 | transformation. 36 | operators_to_add_to_block : a dict of strings and numpy arrays of ndim = 2. 37 | The list of operators to transform. 38 | 39 | Returns 40 | ------- 41 | result : a Block. 42 | A block with the new transformed operators. 43 | """ 44 | cols_of_transformation_matrix = transformation_matrix.shape[1] 45 | result = Block(cols_of_transformation_matrix) 46 | for key in operators_to_add_to_block.keys(): 47 | result.add_operator(key) 48 | result.operators[key] = transform_matrix(operators_to_add_to_block[key], 49 | transformation_matrix) 50 | return result 51 | 52 | class System(object): 53 | """The system class for the DMRG algorithm. 54 | 55 | The system has two blocks, and two single sites, and is the basic 56 | structure you run the algorithm on. Its main function is to put all 57 | these things together to avoid having to pass to every time details 58 | about the underlying chain structure. 59 | 60 | You use this class as a convenience class. 61 | 62 | Examples 63 | -------- 64 | >>> from dmrg101.core.sites import SpinOneHalfSite 65 | >>> from dmrg101.core.system import System 66 | >>> # build a system with four spins one-half. 67 | >>> spin_one_half_site = SpinOneHalfSite() 68 | >>> ising_fm_in_field = System(spin_one_half_site) 69 | >>> # use four strings to name each operator in term 70 | >>> ising_fm_in_field.add_to_hamiltonian('s_z', 's_z', 'id', 'id') 71 | >>> ising_fm_in_field.add_to_hamiltonian('id', 's_z', 's_z', 'id') 72 | >>> ising_fm_in_field.add_to_hamiltonian('id', 'id', 's_z', 's_z') 73 | >>> # use argument names to save extra typing for some terms 74 | >>> h = 0.1 75 | >>> ising_fm_in_field.add_to_hamiltonian(left_block_op='s_z', param=-h) 76 | >>> ising_fm_in_field.add_to_hamiltonian(left_site_op='s_z', param=-h) 77 | >>> ising_fm_in_field.add_to_hamiltonian(right_site_op='s_z', param=-h) 78 | >>> ising_fm_in_field.add_to_hamiltonian(right_block_op='s_z', param=-h) 79 | >>> gs_energy, gs_wf = ising_fm_in_field.calculate_ground_state() 80 | >>> print gs_energy 81 | -0.35 82 | >>> print gs_wf.as_matrix 83 | [[ 0. 0.] 84 | [[ 0. 1.] 85 | """ 86 | def __init__(self, left_site, right_site=None, left_block=None, right_block=None): 87 | """Creates the system with the specified sites. 88 | 89 | Exactly that. If you don't provide all the arguments, the missing 90 | blocks or sites are created from the `left_site` single site 91 | argument. 92 | 93 | Parameters 94 | ---------- 95 | left_site : a Site object. 96 | The site you want to use as a single site at the left. 97 | right_site : a Site object (optional). 98 | The site you want to use as a single site at the right. 99 | left_block : a Block object (optional). 100 | The block you want to use as a single block at the left. 101 | right_block : a Block object (optional). 102 | The block you want to use as a single block at the right. 103 | """ 104 | super(System, self).__init__() 105 | self.left_site = left_site 106 | 107 | if right_site is not None: 108 | self.right_site = right_site 109 | else: 110 | self.right_site = left_site 111 | 112 | if left_block is not None: 113 | self.left_block = left_block 114 | else: 115 | self.left_block = make_block_from_site(left_site) 116 | 117 | if right_block is not None: 118 | self.right_block = right_block 119 | else: 120 | self.right_block = make_block_from_site(left_site) 121 | 122 | self.h = CompositeOperator(self.get_left_dim(), self.get_right_dim()) 123 | self.operators_to_add_to_block = {} 124 | self.old_left_blocks = [] 125 | self.old_right_blocks = [] 126 | # 127 | # start growing on the left, which may look as random as start 128 | # growing on the right, but however the latter will ruin the 129 | # *whole* thing. 130 | # 131 | self.set_growing_side('left') 132 | self.number_of_sites = None 133 | self.model = None 134 | 135 | def clear_hamiltonian(self): 136 | """Makes a brand new hamiltonian. 137 | """ 138 | self.h = CompositeOperator(self.get_left_dim(), self.get_right_dim()) 139 | 140 | def get_left_dim(self): 141 | """Gets the dimension of the Hilbert space of the left block 142 | """ 143 | return self.left_block.dim * self.left_site.dim 144 | 145 | def get_right_dim(self): 146 | """Gets the dimension of the Hilbert space of the right block 147 | """ 148 | return self.right_block.dim * self.right_site.dim 149 | 150 | def get_shriking_block_next_step_size(self, left_block_size): 151 | """Gets the size of the shrinking block in the next DMRG step. 152 | 153 | Gets the size of the shrinking block, i.e. the number of sites 154 | (not including the single site), in the next step of the finite 155 | DMRG algorithm. 156 | 157 | Parameters 158 | ---------- 159 | left_block_size : an int. 160 | The *current*, i.e. at the current DMRG step, number of sites 161 | of the left block (despite the sweep is to the left or the 162 | right.) Again this does not include the single site. 163 | 164 | Returns 165 | ------- 166 | result : a int. 167 | The number of sites of the shrinking block, without including 168 | the single site. 169 | """ 170 | result = None 171 | if self.growing_side == 'left': 172 | result = self.number_of_sites - (left_block_size + 3) 173 | else: 174 | result = left_block_size - 1 175 | if result < 0: 176 | raise DMRGException("Block shrank too much") 177 | return result 178 | 179 | def set_growing_side(self, growing_side): 180 | """Sets which side, left or right, is growing. 181 | 182 | You use this function to change the side which is growing. You 183 | should set the growing side every time you want to change the 184 | direction of the sweeps. 185 | 186 | Parameters 187 | ---------- 188 | growing_side : a string. 189 | Which side, left or right, is growing. 190 | 191 | Raises 192 | ------ 193 | DMRGException 194 | if the `growing_side` is not 'left' or 'right'. 195 | """ 196 | if growing_side not in ('left', 'right'): 197 | raise DMRGException("Bad growing side") 198 | 199 | self.growing_side = growing_side 200 | if self.growing_side == 'left': 201 | self.growing_site = self.left_site 202 | self.growing_block = self.left_block 203 | self.shrinking_site = self.right_site 204 | self.shrinking_block = self.right_block 205 | self.shrinking_side = 'right' 206 | else: 207 | self.growing_site = self.right_site 208 | self.growing_block = self.right_block 209 | self.shrinking_site = self.left_site 210 | self.shrinking_block = self.left_block 211 | self.shrinking_side = 'left' 212 | 213 | def add_to_hamiltonian(self, left_block_op='id', left_site_op='id', 214 | right_site_op='id', right_block_op='id', 215 | param=1.0): 216 | """Adds a term to the hamiltonian. 217 | 218 | You use this function to add a term to the Hamiltonian of the 219 | system. This is just a convenience function. 220 | 221 | Parameters 222 | ---------- 223 | left_block_op : a string (optional). 224 | The name of an operator in the left block of the system. 225 | left_site_op : a string (optional). 226 | The name of an operator in the left site of the system. 227 | right_site_op : a string (optional). 228 | The name of an operator in the right site of the system. 229 | right_block_op : a string (optional). 230 | The name of an operator in the right block of the system. 231 | param : a double/complex (optional). 232 | A parameter which multiplies the term. 233 | 234 | Raises 235 | ------ 236 | DMRGException 237 | if any of the operators are not in the corresponding 238 | site/block. 239 | 240 | Examples 241 | -------- 242 | >>> from dmrg101.core.sites import SpinOneHalfSite 243 | >>> from dmrg101.core.system import System 244 | >>> # build a system with four spins one-half. 245 | >>> spin_one_half_site = SpinOneHalfSite() 246 | >>> ising_fm_in_field = System(spin_one_half_site) 247 | >>> # use four strings to name each operator in term 248 | >>> ising_fm_in_field.add_to_hamiltonian('s_z', 's_z', 'id', 'id') 249 | >>> ising_fm_in_field.add_to_hamiltonian('id', 's_z', 's_z', 'id') 250 | >>> ising_fm_in_field.add_to_hamiltonian('id', 'id', 's_z', 's_z') 251 | >>> # use argument names to save extra typing for some terms 252 | >>> h = 0.1 253 | >>> ising_fm_in_field.add_to_hamiltonian(left_block_op='s_z', param=-h) 254 | >>> ising_fm_in_field.add_to_hamiltonian(left_site_op='s_z', param=-h) 255 | >>> ising_fm_in_field.add_to_hamiltonian(right_site_op='s_z', param=-h) 256 | >>> ising_fm_in_field.add_to_hamiltonian(right_block_op='s_z', param=-h) 257 | """ 258 | left_side_op = make_tensor(self.left_block.operators[left_block_op], 259 | self.left_site.operators[left_site_op]) 260 | right_side_op = make_tensor(self.right_block.operators[right_block_op], 261 | self.right_site.operators[right_site_op]) 262 | self.h.add(left_side_op, right_side_op, param) 263 | 264 | def add_to_operators_to_update(self, name, block_op='id', site_op='id'): 265 | """Adds a term to the hamiltonian. 266 | 267 | You use this function to add an operator to the list of operators 268 | that you need to update. You need to update an operator if it is 269 | going to be part of a term in the Hamiltonian in any later step in 270 | the current sweep. 271 | 272 | Parameters 273 | ---------- 274 | name : a string. 275 | The name of the operator you are including in the list to 276 | update. 277 | left_block_op : a string (optional). 278 | The name of an operator in the left block of the system. 279 | left_site_op : a string (optional). 280 | The name of an operator in the left site of the system. 281 | 282 | Raises 283 | ------ 284 | DMRGException 285 | if any of the operators are not in the corresponding 286 | site/block. 287 | 288 | Examples 289 | -------- 290 | >>> from dmrg101.core.sites import SpinOneHalfSite 291 | >>> from dmrg101.core.system import System 292 | >>> # build a system with four spins one-half. 293 | >>> spin_one_half_site = SpinOneHalfSite() 294 | >>> ising_fm_in_field = System(spin_one_half_site) 295 | >>> # some stuff here..., but the only operator that you need to 296 | >>> # update is 's_z' for the last site of the block. 297 | >>> ising_fm_in_field.add_to_operators_to_update(site_op='s_z') 298 | >>> print ising_fm_in_field.operators_to_add_to_block.keys() 299 | ('s_z') 300 | """ 301 | tmp = make_tensor(self.growing_block.operators[block_op], 302 | self.growing_site.operators[site_op]) 303 | self.operators_to_add_to_block[name] = tmp 304 | 305 | def add_to_block_hamiltonian(self, tmp_matrix_for_bh, block_op='id', 306 | site_op='id', param=1.0): 307 | """Adds a term to the hamiltonian. 308 | 309 | You use this function to add a term to the Hamiltonian of the 310 | system. This is just a convenience function. 311 | 312 | Parameters 313 | ---------- 314 | tmp_matrix_for_bh : a numpy array of ndim = 2. 315 | An auxiliary matrix to keep track of the result. 316 | left_block_op : a string (optional). 317 | The name of an operator in the left block of the system. 318 | left_site_op : a string (optional). 319 | The name of an operator in the left site of the system. 320 | param : a double/complex (optional). 321 | A parameter which multiplies the term. 322 | 323 | Raises 324 | ------ 325 | DMRGException 326 | if any of the operators are not in the corresponding 327 | site/block. 328 | 329 | Examples 330 | -------- 331 | >>> from dmrg101.core.sites import SpinOneHalfSite 332 | >>> from dmrg101.core.system import System 333 | >>> # build a system with four spins one-half. 334 | >>> spin_one_half_site = SpinOneHalfSite() 335 | >>> ising_fm_in_field = System(spin_one_half_site) 336 | >>> # add the previous block Hamiltonian... 337 | >>> ising_fm_in_field.add_to_block_hamiltonian(block_op = 'bh') 338 | >>> # ... and then add the term coming from eating the current site. 339 | >>> ising_fm_in_field.add_to_block_hamiltonian('s_z', 's_z') 340 | """ 341 | tmp = make_tensor(self.growing_block.operators[block_op], 342 | self.growing_site.operators[site_op]) 343 | tmp_matrix_for_bh += param * tmp 344 | 345 | def update_all_operators(self, transformation_matrix): 346 | """Updates the operators and puts them in the block. 347 | 348 | You use this function to actually create the operators that are 349 | going to make the block after a DMRG iteration. 350 | 351 | Parameters 352 | ---------- 353 | transformation_matrix : a numpy array of ndim = 2. 354 | 355 | Returns 356 | ------- 357 | result : a Block. 358 | A new block 359 | """ 360 | if self.growing_side == 'left': 361 | self.old_left_blocks.append(deepcopy(self.left_block)) 362 | self.left_block = make_updated_block_for_site( 363 | transformation_matrix, self.operators_to_add_to_block) 364 | else: 365 | self.old_right_blocks.append(deepcopy(self.right_block)) 366 | self.right_block = make_updated_block_for_site( 367 | transformation_matrix, self.operators_to_add_to_block) 368 | 369 | def set_block_to_old_version(self, shrinking_size): 370 | """Sets the block for the shriking block to an old version. 371 | 372 | Sets the block for the shriking block to an old version. in 373 | preparation for the next DMRG step. You use this function in the 374 | finite version of the DMRG algorithm to set an shriking block to 375 | an old version. 376 | 377 | Parameters 378 | ---------- 379 | shrinking_size : an int. 380 | The size (not including the single site) of the shrinking side 381 | in the *next* step of the finite algorithm. 382 | """ 383 | if shrinking_size == 0: 384 | raise DMRGException("You need to turn around") 385 | if self.shrinking_side == 'left': 386 | self.left_block = self.old_left_blocks[shrinking_size-1] 387 | else: 388 | self.right_block = self.old_right_blocks[shrinking_size-1] 389 | 390 | def calculate_ground_state(self, initial_wf=None, min_lanczos_iterations=3, 391 | too_many_iterations=1000, precision=0.000001): 392 | """Calculates the ground state of the system Hamiltonian. 393 | 394 | You use this function to calculate the ground state energy and 395 | wavefunction for the Hamiltonian of the system. The ground state 396 | is calculated using the Lanczos algorithm. This is again a 397 | convenience function. 398 | 399 | Parameters 400 | ---------- 401 | initial_wf : a Wavefunction, optional 402 | The wavefunction that will be used as seed. If None, a random one 403 | if used. 404 | min_lanczos_iterations : an int, optional. 405 | The number of iterations before starting the diagonalizations. 406 | too_many_iterations : a int, optional. 407 | The maximum number of iterations allowed. 408 | precision : a double, optional. 409 | The accepted precision to which the ground state energy is 410 | considered not improving. 411 | 412 | Returns 413 | ------- 414 | gs_energy : a double. 415 | The ground state energy. 416 | gs_wf : a Wavefunction. 417 | The ground state wavefunction (normalized.) 418 | """ 419 | return lanczos.calculate_ground_state(self.h, initial_wf, 420 | min_lanczos_iterations, 421 | too_many_iterations, precision) 422 | 423 | def get_truncation_matrix(self, ground_state_wf, number_of_states_kept): 424 | """Grows one side of the system by one site. 425 | 426 | Calculates the truncation matrix by calculating the reduced density 427 | matrix for `ground_state_wf` by tracing out the degrees of freedom of 428 | the shrinking side. 429 | 430 | Parameters 431 | ---------- 432 | ground_state_wf : a Wavefunction. 433 | The ground state wavefunction of your system. 434 | number_of_states_kept : an int. 435 | The number of states you want to keep in each block after the 436 | truncation. If the `number_of_states_kept` is smaller than the 437 | dimension of the current Hilbert space block, all states are kept. 438 | 439 | Returns 440 | ------- 441 | truncation_matrix : a numpy array with ndim = 2. 442 | The truncation matrix from the reduced density matrix 443 | diagonalization. 444 | entropy : a double. 445 | The Von Neumann entropy for the cut that splits the chain into two 446 | equal halves. 447 | truncation_error : a double. 448 | The truncation error, i.e. the sum of the discarded eigenvalues of 449 | the reduced density matrix. 450 | """ 451 | rho = ground_state_wf.build_reduced_density_matrix(self.shrinking_side) 452 | evals, evecs = diagonalize(rho) 453 | truncated_evals, truncation_matrix = truncate(evals, evecs, 454 | number_of_states_kept) 455 | entropy = calculate_entropy(truncated_evals) 456 | truncation_error = calculate_truncation_error(truncated_evals) 457 | return truncation_matrix, entropy, truncation_error 458 | 459 | def grow_block_by_one_site(self, truncation_matrix): 460 | """Grows one side of the system by one site. 461 | 462 | Uses the truncation matrix to update the operators you need in the 463 | next steps, effectively growing the size of the block by one site. 464 | 465 | Parameters 466 | ---------- 467 | truncation_matrix : a numpy array with ndim = 2. 468 | The truncation matrix from the reduced density matrix 469 | diagonalization. 470 | """ 471 | self.set_block_hamiltonian() 472 | self.set_operators_to_update() 473 | self.update_all_operators(truncation_matrix) 474 | 475 | def set_hamiltonian(self): 476 | """Sets a system Hamiltonian to the model Hamiltonian. 477 | 478 | Just a wrapper around the corresponding `Model` method. 479 | """ 480 | self.model.set_hamiltonian(self) 481 | 482 | def set_block_hamiltonian(self): 483 | """Sets the block Hamiltonian to model block Hamiltonian. 484 | 485 | Just a wrapper around the corresponding `Model` method. 486 | """ 487 | tmp_matrix_size = None 488 | if self.growing_side == 'left': 489 | tmp_matrix_size = self.get_left_dim() 490 | else: 491 | tmp_matrix_size = self.get_right_dim() 492 | tmp_matrix_for_bh = np.zeros((tmp_matrix_size, tmp_matrix_size)) 493 | self.model.set_block_hamiltonian(tmp_matrix_for_bh, self) 494 | self.operators_to_add_to_block['bh'] = tmp_matrix_for_bh 495 | 496 | def set_operators_to_update(self): 497 | """Sets the operators to update to be what you need to AF Heisenberg. 498 | 499 | Just a wrapper around the corresponding `Model` method. 500 | """ 501 | self.model.set_operators_to_update(self) 502 | 503 | def infinite_dmrg_step(self, left_block_size, number_of_states_kept): 504 | """Performs one step of the (asymmetric) infinite DMRG algorithm. 505 | 506 | Calculates the ground state of a system with a given size, then 507 | performs the DMRG transformation on the operators of *one* block, 508 | therefore increasing by one site the number of sites encoded in the 509 | Hilbert space of this block, and reset the block in the system to be 510 | the new, enlarged, truncated ones. The other block is kept one-site 511 | long. 512 | 513 | Parameters 514 | ---------- 515 | left_block_size : an int. 516 | The number of sites of the left block, not including the single site. 517 | number_of_states_kept : an int. 518 | The number of states you want to keep in each block after the 519 | truncation. If the `number_of_states_kept` is smaller than the 520 | dimension of the current Hilbert space block, all states are kept. 521 | 522 | Returns 523 | ------- 524 | energy : a double. 525 | The energy for the `current_size`. 526 | entropy : a double. 527 | The Von Neumann entropy for the cut that splits the chain into two 528 | equal halves. 529 | truncation_error : a double. 530 | The truncation error, i.e. the sum of the discarded eigenvalues of 531 | the reduced density matrix. 532 | 533 | Notes 534 | ----- 535 | This asymmetric version of the algorithm when you just grow one of the 536 | block while keeping the other one-site long, is obviously less precise 537 | than the symmetric version when you grow both sides. However as we are 538 | going to sweep next using the finite algorithm we don't care much 539 | about precision at this stage. 540 | """ 541 | self.set_growing_side('left') 542 | self.set_hamiltonian() 543 | ground_state_energy, ground_state_wf = self.calculate_ground_state() 544 | truncation_matrix, entropy, truncation_error = ( 545 | self.get_truncation_matrix(ground_state_wf, 546 | number_of_states_kept) ) 547 | if left_block_size == self.number_of_sites - 3: 548 | self.turn_around('right') 549 | else: 550 | self.grow_block_by_one_site(truncation_matrix) 551 | return ground_state_energy, entropy, truncation_error 552 | 553 | def finite_dmrg_step(self, growing_side, left_block_size, 554 | number_of_states_kept): 555 | """Performs one step of the finite DMRG algorithm. 556 | 557 | Calculates the ground state of a system with a given size, then 558 | performs the DMRG transformation on the operators of *one* block, 559 | therefore increasing by one site the number of sites encoded in the 560 | Hilbert space of this block, and reset the block in the system to be 561 | the new, enlarged, truncated ones. The other block is read out from 562 | the previous sweep. 563 | 564 | Parameters 565 | ---------- 566 | growing_side : a string. 567 | Which side, left or right, is growing. 568 | left_block_size : an int. 569 | The number of sites in the left block in the *current* step, not 570 | including the single site. 571 | number_of_states_kept : an int. 572 | The number of states you want to keep in each block after the 573 | truncation. If the `number_of_states_kept` is smaller than the 574 | dimension of the current Hilbert space block, all states are kept. 575 | 576 | Returns 577 | ------- 578 | energy : a double. 579 | The energy at this step. 580 | entropy : a double. 581 | The Von Neumann entropy for the cut at this step. 582 | truncation_error : a double. 583 | The truncation error, i.e. the sum of the discarded eigenvalues of 584 | the reduced density matrix. 585 | 586 | Raises 587 | ------ 588 | DMRGException 589 | if `growing_side` is not 'left' or 'right'. 590 | 591 | Notes 592 | ----- 593 | This asymmetric version of the algorithm when you just grow one of the 594 | block while keeping the other one-site long, is obviously less precise 595 | than the symmetric version when you grow both sides. However as we are 596 | going to sweep next using the finite algorithm we don't care much 597 | about precision at this stage. 598 | """ 599 | if growing_side not in ('left', 'right'): 600 | raise DMRGException('Growing side must be left or right.') 601 | 602 | self.set_growing_side(growing_side) 603 | self.set_hamiltonian() 604 | ground_state_energy, ground_state_wf = self.calculate_ground_state() 605 | truncation_matrix, entropy, truncation_error = ( 606 | self.get_truncation_matrix(ground_state_wf, 607 | number_of_states_kept) ) 608 | shrinking_size = self.get_shriking_block_next_step_size(left_block_size) 609 | if shrinking_size == 0: 610 | self.turn_around(self.shrinking_side) 611 | else: 612 | self.grow_block_by_one_site(truncation_matrix) 613 | self.set_block_to_old_version(shrinking_size) 614 | return ground_state_energy, entropy, truncation_error 615 | 616 | def turn_around(self, new_growing_side): 617 | """Turns around in the finite algorithm. 618 | 619 | When you reach the smallest possible size for your shriking block, 620 | you must turn around and start sweeping in the other direction. 621 | This is just done by setting the to be growing side, which 622 | currently is skrinking, to be made up of a single site, and by 623 | setting the to be shrinking side, which now is growing to be the 624 | current growing block. 625 | 626 | Parameters 627 | ---------- 628 | new_growing_side : a string. 629 | The side that will start growing. 630 | """ 631 | if new_growing_side == 'left': 632 | self.left_block = make_block_from_site(self.left_site) 633 | self.old_left_blocks = [] 634 | else: 635 | self.right_block = make_block_from_site(self.right_site) 636 | self.old_right_blocks = [] 637 | --------------------------------------------------------------------------------