git

使用案例

新建仓库

   git init .

下载远程仓库

   git clone --depth=1 https://...

--depth=1 表示只下载一部分历史

gitignore:忽略文件

gitignore

添加本地更改

   git add --all 
   git commit -m "sth"

从远端获取更新

   git pull

推送更新到远端

   git push origin master

使用代理

   git config --global http.proxy http://proxy.mycompany:80

记录里显示中文

   git config --global core.quotepath false

设置默认编辑器

   git config --global core.editor "vim"

git-lfs: 用 git 管理大文件

安装

从包管理器中安装 git-lfs ,windows版本已经包含了

使用

  1. 对账户启用 git lfs install
  2. 对仓库设置要追踪的文件类型 git lfs track "*.psd"
  3. 把配置文件加到追踪列表里 git add .gitattributes

常用大文件

gitattributes

   git lfs track "*.png"
   git lfs track "*.jpg"
   git lfs track "*.jpeg"
   git lfs track "*.gif"

   git lfs track "*.mp4"
   git lfs track "*.mpeg"
   git lfs track "*.mkv"

.gitattributes 文件内容为

*.pdf   filter=lfs diff=lfs merge=lfs -text
*.jpg   filter=lfs diff=lfs merge=lfs -text
*.jpeg  filter=lfs diff=lfs merge=lfs -text
*.png   filter=lfs diff=lfs merge=lfs -text
*.gif   filter=lfs diff=lfs merge=lfs -text
*.ico   filter=lfs diff=lfs merge=lfs -text
*.eps   filter=lfs diff=lfs merge=lfs -text
*.bmp   filter=lfs diff=lfs merge=lfs -text
*.psb   filter=lfs diff=lfs merge=lfs -text
*.tiff  filter=lfs diff=lfs merge=lfs -text
*.wbmp  filter=lfs diff=lfs merge=lfs -text
*.webp  filter=lfs diff=lfs merge=lfs -text

git-remote-dropbox: 用 Dropbox 做私人仓库

安装

  1. 安装 pip install git-remote-dropbox
  2. 在网站上生成 OAuth 2 密钥 app console
  3. 把密钥存在文件 /.config/git/git-remote-dropbox.json or ~/.git-remote-dropbox.json ~{"default":"token"}
  4. 配置别名 git config --global alias.dropbox '!git-dropbox-manage'

使用

clone 已有仓库

    git clone "dropbox:///path/to/repo"

创建远程仓库

    git remote add origin "dropbox:///path/to/repo"

pdftk: The PDF Toolkit

添加目录与修改元信息

从pdf中提取目录和元信息

   pdftk C:\Users\Sid\Desktop\doc.pdf dump_data output C:\Users\Sid\Desktop\doc_data.txt

给pdf添加或更新目录

   pdftk C:\Users\Sid\Desktop\doc.pdf update_info C:\Users\Sid\Desktop\doc_data.txt output C:\Users\Sid\Desktop\updated.pdf

目录文件格式:

   BookmarkBegin
   BookmarkTitle: PDF Reference (Version 1.5)
   BookmarkLevel: 1
   BookmarkPageNumber: 1
   BookmarkBegin
   BookmarkTitle: Contents
   BookmarkLevel: 2
   BookmarkPageNumber: 3

其中字符应编码成 html entitle 的样子。 可以用 https://tool.oschina.net/encode , https://www.online-toolz.com/tools/unicode-html-entities-convertor.php 这里的 Native/Unicode 转换工具。

或者python

   u"阿斯顿".encode('ascii', 'xmlcharrefreplace')

   def utf8_to_ascii_xml(filename):
       with open(filename,'r',encoding='utf-8') as fi:
           lines = fi.readlines()
           lines_ascii = [x.encode('ascii', 'xmlcharrefreplace') for x in lines]
       with open(f"{filename}.ascii", 'wb') as fi:
           fi.writelines(lines_ascii)

常用正则替换

s/\(.*\),\(.*\)/BookmarkBegin\nBookmarkTitle: \1\nBookmarkLevel: 2\nBookmarkPageNumber: \2/

添加目录的示例流程

   # import
   import pytesseract
   from PIL import Image
   import numpy as np
   import pandas as pd
   import re

   # 利用 OCR 生成目录
   print(pytesseract.image_to_string(Image.open('a.png')))

   # 手动编辑目录,使用 orgmode 的表格
   data = pd.read_csv("a.txt", sep="|")
   darr = np.array(data)
   darr[:,1] = [i.strip() for i in darr[:,1]]
   darr[:,2] = [i.strip() for i in darr[:,2]]
   darr[2:,3] = darr[2:,3]+9
   data = pd.DataFrame(darr[:,1:-1])
   data

   # 生成目录格式并保存
   block = """BookmarkBegin
   BookmarkTitle: {title}
   BookmarkLevel: {level}
   BookmarkPageNumber: {page}
   """
   txt = ""
   for line in darr:
       index = line[1]
       title = line[2]
       page = line[3]
       if index == '' or index[-1] == '.':
           level = 1
       else:
           level = 2
           txt += block.format(title=f"{index} {title}", level=level, page=page)

   with open("b.txt",'w') as fi:
       fi.writelines(txt)

合并pdf文件

  pdftk in1.pdf in2.pdf cat output out1.pdf

djvu to pdf

  1. djvulibre
  ddjvu -format=pdf -quality=85 -verbose a.djvu a.pdf

分割文件

pdftk in.pdf burst output out_%04d.pdf

Early History of Bosonization

本文是 A. O. Gogolin, A. A. Nersesyan, A. M. Tsvelik 的 "Bosonization and Strongly Correlated Systems" 一书的序言,简短地回顾了强关联领域和1990年以前的玻色化方法的历史,对了解强关联领域很有帮助。

玻色化的早期历史

ssh

SSH 端口转发

本地转发: 本地访问远程

本地转发,顾名思义就是把本地主机端口通过待登录主机端口转发到远程主机端口上去。

本地转发通过参数 -L 指定,格式:-L [本地主机:]本地主机端口:远程网络主机:远程网络主机端口。加上ssh待登录主机,这里就有了三台主机。

举例:~ssh -L 0.0.0.0:50000:host2:80 user@host1~ 。这条命令将host2的80端口映射到本地的50000端口,前提是待登录主机host1上可以正常连接到host2的80端口。

畅想一下这个功能的作用:

  1. 因为本地的mysql更顺手,想用本地的mysql客户端命令连接受限网络环境的mysql服务端。
  2. 本地安装了开发工具,想用这个开发工具连接受限网络环境中某个服务的远程调试端口。

远程转发: 远程访问本地

远程转发是指把登录主机所在网络中某个端口通过本地主机端口转发到远程主机上。

远程转发通过参数 -R 指定,格式:-R [登录主机:]登录主机端口:本地网络主机:本地网络主机端口。

举例:~ssh -R 0.0.0.0:8080:host2:80 user@host1~ 。这条命令将host2的80端口映射到待登录主机host1的8080端口,前提是本地主机可以正常连接host2的80端口。

畅想一下这个功能的作用:

  1. 本地网络中有一个http代理,通过这个代理可以上外网,因此通过这条命令将这个http代理映射到待登录主机的某个端口,这样受限网络环境中所有其它服务器即可使用这个http代理上外网了。
  2. 在本机开发了一个web应用,想拿给别人测试,但现在你却处在内网,外网是无法直接访问内网的主机的,怎么办!?很多人可能会说,找台有公网IP的主机,重新部署一下就行了。这样可行,但太麻烦。然而自从你了解了ssh的远程转发之后,一切都变得简单了。只需在本地主机上执行一下上面例子的命令即可实现外网访问内网的web应用。

注意:

  1. sshdconfig里要打开AllowTcpForwarding选项,否则-R远程端口转发会失败。
  2. 默认转发到远程主机上的端口绑定的是127.0.0.1,如要绑定0.0.0.0需要打开sshdconfig里的GatewayPorts选项。这个选项如果由于权限没法打开也有办法,可配合ssh -L将端口绑定到0.0.0.0,聪明的你应该能想到办法,呵呵。

动态转发

相对于本地转发和远程转发的单一端口转发模式而言,动态转发有点更加强劲的端口转发功能,即是无需固定指定被访问目标主机的端口号。这个端口号需要在本地通过协议指定,该协议就是简单、安全、实用的 SOCKS 协议。

动态转发通过参数 -D 指定,格式:-D [本地主机:]本地主机端口。相对于前两个来说,动态转发无需再指定远程主机及其端口。它们由通过 SOCKS协议 连接到本地主机端口的那个主机。

举例:~ssh -D 50000 user@host1~ 。这条命令创建了一个SOCKS代理,所以通过该SOCKS代理发出的数据包将经过host1转发出去。

怎么使用?

  1. 用firefox浏览器,在浏览器里设置使用socks5代理127.0.0.1:50000,然后浏览器就可以访问host1所在网络内的任何IP了。
  2. 如果是普通命令行应用,使用proxychains-ng,参考命令如下:

          brew install proxychains-ng
          vim /usr/local/etc/proxychains.conf # 在ProxyList配置段下添加配置 "socks5 	127.0.0.1 50000"
          proxychains-ng wget http://host2 # 在其它命令行前添加proxychains-ng即可
    
  3. 如果是ssh,则用以下命令使用socks5代理:

          ssh -o ProxyCommand='/usr/bin/nc -X 5 -x 127.0.0.1:5000 %h %p' user@host2
    

畅想一下这个功能的作用:

  1. 想访问受限网络环境中的多种服务
  2. FQ
  3. ……

免密码登录

在本地机器生成密钥对

   ssh-keygen -t rsa

根据提示设置文件名和密码

授权登录

将公钥追加到目标服务器的 $HOME/.ssh/authorized_keys 文件中,该文件的权限应为 600.ssh 目录权限应为 700

   touch ~/.ssh/authorized_keys
   chmod 600 ~/.ssh/authorized_keys
   cat ~/.ssh/a_rsa.pub >> ~/.ssh/authorized_keys

测试一下

   ssh username@localhost

配置别名

~/.ssh/config 中配置别名,就不用记 ip 了。

  # 指定使用 bash 登录
  Host thbash
      HostName 10.0.0.101
      User username
      RequestTTY yes
      RemoteCommand bash

  # 指定使用 zsh 登录
  Host th
      HostName 10.0.0.101
      User username
      RequestTTY yes
      RemoteCommand zsh

  # 最基本的设置
  Host group1
      Hostname 10.0.5.90
      User username

systemd

systemctl: 管理服务

  • systemd 中的服务后缀名 .service 可以不写,会自动补全。
  • systemd 中把服务称作 unit , unit 有多种类型包括 .service, .target

列出服务

   # 查看系统所有安装的服务项
   systemctl list-unit-files --type=service

   # 查看系统所有运行的服务项
   systemctl list-units --type=service

   # 查看系统所有开机自启动的服务项
   systemctl list-unit-files --type=service | grep enabled

查看特定服务

   # 查看指定服务项状态
   systemctl status <服务项名称>

   # 查看服务项的依赖关系
   systemctl list-dependencies <服务项名称>

查看系统状态

   # 查看系统启动耗时
   systemd-analyze

   # 查看各项服务启动耗时
   systemd-analyze blame | grep .service

管理服务

   # 启动服务
   systemctl start <服务项名称>

   # 停止服务
   systemctl stop <服务项名称>

   # 重启服务
   systemctl restart <服务项名称>

   # 重新读取配置文件
   # 如果该服务不能重启,但又必须使用新的配置,这条命令会很有用
   systemctl reload <服务项名称>

   # 使服务开机自启动
   systemctl enable <服务项名称>

   # 使服务不要开机自启动
   systemctl disable <服务项名称>

   # 禁用服务
   # 这可以防止服务被其他服务间接启动,也无法通过 start 或 restart 命令来启动服务。
   systemctl mask <服务项名称>

   # 不再禁用服务
   systemctl unmask <服务项名称>

   # 重新读取所有服务项
   # 修改、添加、删除服务项之后需要执行以下命令。
   systemctl daemon-reload

编写简单的服务

服务文件都存放在 /etc/systemd/system/ 中,后缀名为 .service

简单的自启动脚本

  • Type=simple 表示启动并一直运行
  • ExecStart=/... 表示要执行的命令,路径要写全
  • WantedBy=multi-user.target 表示当用户能登录的时候就该启动这个服务了
  • User=name 表示运行程序时使用的用户,默认为 root
   [Unit]
   Description=My Startup

   [Service]
   Type=simple
   ExecStart=/path/to/server --server

   [Install]
   WantedBy=multi-user.target

配置自启动 jupyterlab

/etc/systemd/system/jupyterlab.service

   # start jupyter lab
   [Unit]
   Description=jupyter lab
   After=nginx.target

   [Service]
   Type=simple
   Environment="PATH='/home/username/.local/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games'" "PYTHONPATH='/home/username/.local/bin:/usr/lib/python39.zip:/usr/lib/python3.9:/usr/lib/python3.9/lib-dynload:/home/username/.local/lib/python3.9/site-packages:/usr/local/lib/python3.9/dist-packages:/usr/lib/python3/dist-packages:/home/username/.local/lib/python3.9/site-packages/IPython/extensions:/home/username/.ipython'"
   ExecStart=/home/username/.local/bin/jupyter lab --LabApp.app_dir=/home/username/.local/share/jupyter/lab --notebook-dir=/home/username/Projects --allow-root --config=/home/username/.jupyter/jupyter_lab_config.py

   [Install]
   WantedBy=multi-user.target

配置自动挂载

配置文件路径: /etc/systemd/system/path-to-dir.mount

注意:文件名必须是挂载路径名,例如要把 /dev/sdb1 挂载到 /mnt/sds ,配置文件应该叫做 mnt-sds.mount

   [Unit]
   Description=mount 

   [Mount]
   What=/dev/sdb1
   Where=/mnt/sds
   Options=defaults

   [Install]
   WantedBy=local-fs.target

问题

用户定义服务

  • 命令使用 systemctl --user 剩下一样。
  • 配置文件放在 $HOME/.config/systemd/user
  • 然后执行一次 loginctl enable-linger username 让开机时自启动用户

samba 文件共享

Samba 基本配置

安装 Samba

   apt install samba

添加组和用户

假定已经有一个本地用户 mylocaluser

   # 创建 smb 的组
   /usr/sbin/addgroup smbgroup
   # 把用户加到组里
   /usr/sbin/usermod -aG smbgroup mylocaluser
   # 设置用户的远程密码,可以与本地登录密码不同
   smbpasswd -a mylocaluser

配置文件

首先一定要备份一份

   cp /etc/samba/smb.conf /etc/samba/smb.conf.backup

配置文件修改如下,没有的项目就加进去,已有的就修改一下

   [global]

   # 允许用户登录
   security = user

   [homes]

   read only = no

如果要加入其它登录地址,例如 smb://ip/newport 就如下配置

   [newport]
   path = /path/to/files
   valid users = @smbgroup
   guest ok = no
   writable = yes
   browsable = yes

软链接权限

从别处软链接过来的文件夹打不开,因为没有配置软链接的权限,只要在 [global] 里加入下面的内容就行了。

参考:这个博客

  [global]
  wide links = yes
  symlinks = yes
  unix extensions = no

Exact Diagonalization

精确对角化

参考材料:

  1. Anders W. Sandvik 的综述 Computational Studies of Quantum Spin Systems
  2. 以及他的课程ppt XIV Training Course in the Physics of Strongly Correlated Systems
  3. Ralf Schneider, et.al. "Computational Many-Particle Physics" 的第 18 章

精确对角化实际上就是把哈密顿量写成矩阵表示,然后再求这个矩阵的最小本征值和本征矢,也就是基态和基态能量。

对于单电子图像的哈密顿量是很容易表示成矩阵形式的,多体哈密顿量的直接表示也不是很难。但是由于多体系统随着尺寸增加,矩阵的维数迅速增大,很容易就会超过计算机甚至超算的内存能力。所以精确对角化方法的核心就是如何利用系统的对称性把哈密顿量的矩阵划分成更小的矩阵。

下面先以海森堡自旋 $\frac{1}{2}$ 链为例说明如何利用对称性划分哈密顿量矩阵。最后再给出一个利用这些对称性的 Fermi-Hubbard 模型例子。其它所有一维电子模型都可以用与这两个例子类似的方法实现。

直接表示哈密顿量的矩阵

Heisenberg $S= \frac{1}{2}$ 链 $$ H = J\sum_{i=1}^{N} S_i \cdot S_{i+1} = J \sum_{i=1}^{N} \left( S_i^xS_{i+1}^x+S_i^yS_{i+1}^y+S_i^zS_{i+1}^z \right) $$ $$ H = J \sum_{i=1}^{N} \left[ \frac{1}{2} \left( S_i^+S_{i+1}^-+S_i^-S_{i+1}^+ \right) + S_i^zS_{i+1}^z \right] $$

为了简化起见,下面都取 $J=1$ ,即反铁磁Heisenberg模型。

算符张量积构造哈密顿量

一般单粒子的哈密顿量都会由算符的张量积构造得到,如下

In [1]:
import numpy as np


def tensor_product(*args):
    if len(args) < 2:
        return args[0]

    result = args[0]
    for i in range(1, len(args)):
        result = np.kron(result, args[i])
    return result

首先定义每个格点上的算符

In [2]:
Sz = np.array([[1, 0], [0, -1]])
Sp = np.array([[0, 1], [0, 0]])
Sm = np.array([[0, 0], [1, 0]])
II = np.eye(2)

接下来把所有格点的项全部加起来,这里以4个格点为例

In [3]:
H = (
    1 / 2 * (tensor_product(Sp, Sm, II, II) + tensor_product(Sm, Sp, II, II))
    + tensor_product(Sz, Sz, II, II)
    + 1 / 2 * (tensor_product(II, Sp, Sm, II) + tensor_product(II, Sm, Sp, II))
    + tensor_product(II, Sz, Sz, II)
    + 1 / 2 * (tensor_product(II, II, Sp, Sm) + tensor_product(II, II, Sm, Sp))
    + tensor_product(II, II, Sz, Sz)
    + 1 / 2 * (tensor_product(Sm, II, II, Sp) + tensor_product(Sp, II, II, Sm))
    + tensor_product(Sz, II, II, Sz)
)
H111 = H

有了哈密顿量矩阵就可以对它进行对角化求基态了,这里调用 Arpack 求最小的本征值

In [4]:
from scipy.sparse.linalg import eigsh

eigvals, eigvecs = eigsh(H, k=1, which="SA")
eigvals
Out[4]:
array([-4.44948974])

根据基矢构造哈密顿量

从上面最一般的过程能看出,构造哈密顿量需要做大量的张量积计算,而且这些张量积中乘的项大多数都是单位矩阵,也就是说哈密顿量矩阵是非常稀疏的,所以我们其实可以用稀疏矩阵来表示哈密顿量,并且直接找到有数字的那些基矢,往哈密顿量里填数字就行了。

在上面的张量积构造过程中我们实际上取的表象是按照格点顺序排列的,即 $$ \begin{array}{lll} |0\rangle &= |\uparrow\uparrow\uparrow\uparrow\rangle &= (0000) \\ |1\rangle &= |\uparrow\uparrow\uparrow\downarrow\rangle &= (0001) \\ |2\rangle &= |\uparrow\uparrow\downarrow\uparrow\rangle &= (0010) \\ |3\rangle &= |\uparrow\uparrow\downarrow\downarrow\rangle &= (0011) \\ \cdots &= \cdots &= \cdots \end{array} $$ 很容易发现基矢的表示自然地与数字的二进制联系起来了,因为每个格点恰好也只有两种可能的基矢,上下自旋分别对应于数字0和1。

这样我们就得到了一个关系 $$ \begin{array}{llll} S^z &\Rightarrow & 0\rightarrow 0 & 1\rightarrow 1 \\ S^- &\Rightarrow & 0\rightarrow \emptyset & 1\rightarrow 0 \\ S^+ &\Rightarrow & 0\rightarrow 1 & 1\rightarrow \emptyset \end{array} $$ 即,$S^z$ 作用无效果,$S^-$ 相当于如果bit是0就翻转为1,如果是1,这个基矢就消失掉。

综合起来,对于最近邻的两个态的四种情况,哈密顿量的作用结果为 $$ \begin{array}{lllll} & (00) & (01) & (10) & (11) \\ S_i^zS_{i+1}^z & (00) & -1(01) & -1(10) & (11) \\ S_i^+S_{i+1}^- &\emptyset & (10) &\emptyset &\emptyset \\ S_i^-S_{i+1}^+ &\emptyset &\emptyset & (01) &\emptyset \end{array} $$

接下来只要遍历所有的基矢,并在每个基矢上作用一遍哈密顿量的所有项,找到对应的基矢,把这两个基矢作为指标填上数字就行了。

In [5]:
def get_state_index(state: int, index: int) -> int:
    """获得一个整数某位置处的二进制值"""
    mask = 1 << index
    return (state & mask) >> index


def flip_state(state: int, index: int) -> int:
    """翻转一个整数某位置处的二进制值"""
    mask = 1 << index
    return state ^ mask
In [6]:
N = 4
H = np.zeros((2 ** N, 2 ** N))

# a is a basis
for a in range(2 ** N):
    # i is position of this basis
    for i in range(N):
        # j is the nearest neighbor, mod N for periodic boundary conditions
        j = (i + 1) % N
        ai = get_state_index(a, i)
        aj = get_state_index(a, j)

        # Sz
        b = a
        if ai == aj:
            H[a, b] += 1
        else:
            H[a, b] += -1

        # SxSx + SySy
        if ai != aj:
            b = flip_state(a, i)
            b = flip_state(b, j)
            H[a, b] += 1 / 2

H112 = H

这样直接构造的结果与张量积乘出来的哈密顿量当然是一样的

In [7]:
eigvals, eigvecs = eigsh(H, k=1, which="SA")
eigvals, np.allclose(H112 - H111, 0)
Out[7]:
(array([-4.44948974]), True)

利用对称性:组合型

从基矢构造哈密顿量的过程实际上分为两步,首先要确定基矢的总数和表象,之前我们取得表象就是最自然的全部排列,一共有 $2^N$ 个,然后把这些基矢按照哈密顿量的规则变换成另一组基矢,两组基矢作为哈密顿量矩阵的两个指标来填数字。

利用对称性主要就是改变前一步,不同的对称性子空间实际上限制的是基矢的数目,只要我们把某个子空间中的全部基矢找到,再用这组基矢构造哈密顿量,得到的就是这个对称性子空间中的哈密顿量了。只要把所有子空间都对角化一遍,自然就能得到全部的本征态。

这里先利用的对称性是最简单的一类,这些对称性只与基矢的总数有关,而与基矢内部的排列方式无关,例如总磁矩和总粒子数。总磁矩就是把每个格点上的磁矩全部加起来,总粒子数同样就是把每个格点上的粒子数加起来。总和一样的那些基矢就全部属于同一个子空间。反过来看,也可以是一个总的量子数在所有格点上的不同组合。

这里利用的是Heisenberg模型的总磁矩守恒 $$S^z_{\mathrm{tot}} = \sum_i s[i] $$ 例如 $|0011\rangle, |1010\rangle$ 这两个态的总磁矩就是一样的,都是 $1$。

所以找到总磁矩为 1 的所有基矢有两种方法,一是遍历一遍所有的态,二是按照组合数的方式找到N个格点中取2个的所有组合。

In [8]:
N = 4
# 这里的总自旋写2是因为总的因子 1/2 被省略了
Nz = 2

state_list = []
for a in range(2 ** N):
    if bin(a).count("1") == Nz:
        state_list.append(a)

state_list
Out[8]:
[3, 5, 6, 9, 10, 12]
In [9]:
from itertools import combinations

state_list = [sum([1 << i for i in c]) for c in combinations(range(N), Nz)]
state_list.sort()
state_list
Out[9]:
[3, 5, 6, 9, 10, 12]

接下来就是利用已经找到的这些基矢来构造哈密顿量,构造的过程完全一样,只是要注意,判断基矢规则时要用原来的基矢,之后要把这个基矢对应到新的子空间基矢里。

In [10]:
H = np.zeros((len(state_list),) * 2)

for new_a, a in enumerate(state_list):
    for i in range(N):
        j = (i + 1) % N
        ai = get_state_index(a, i)
        aj = get_state_index(a, j)

        # Sz
        new_b = new_a
        if ai == aj:
            H[new_a, new_b] += 1
        else:
            H[new_a, new_b] += -1

        # SxSx + SySy
        if ai != aj:
            b = flip_state(a, i)
            b = flip_state(b, j)
            new_b = state_list.index(b)
            H[new_a, new_b] += 1 / 2

可以发现对于4格点周期边界情况,基态是在总自旋为1的空间里的,因为这是个反铁磁的Heisenberg链。

In [11]:
eigvals, eigvecs = eigsh(H, k=1, which="SA")
eigvals
Out[11]:
array([-4.44948974])

利用对称性:平移对称性

一维周期边界的链满足平移对称性,平移算符的本征态可以表示为 $$T | n \rangle = e^{ik}|n\rangle, k = m \frac{2\pi}{N}, m=0,1,\cdots,N-1$$ 平移算符的作用就是把态在格点上移动一个 $$T | S^z_1, S^z_2, \cdots, S^z_N \rangle = | S^z_N, S^z_1, \cdots, S^z_{N-1} \rangle $$

在平移算符的本征子空间中的基矢可以由投影算符构造 $$ P_k = \frac{1}{L} \sum_r^{L-1} e^{ i \frac{2\pi}{L} k r } T^r$$ 例如从 $|0011\rangle$ 生成的一组基矢为 $$ P_k |0011\rangle = \frac{1}{4} \left( |0011\rangle + e^{i \frac{2\pi}{4} k \cdot 1} |0110\rangle + e^{i \frac{2\pi}{4} k \cdot 2} |1100\rangle + e^{i \frac{2\pi}{4} k \cdot 3} |1001\rangle \right) $$ 这样就能够得到一组基矢,可以记作 $|r^a(k)\rangle=P_k|r^a\rangle$ ,对于其它的初始基矢还可以得到其它组基矢,记作 $|r^b(k)\rangle=P_k|r^b\rangle$ 等。

那么在这一组新的基矢 $\left\lbrace|r(k)\rangle\right\rbrace$ 下的哈密顿量一共有$L$ 个,分别对应 $k=0,1,\cdots,L-1$,它的矩阵元为 $$ H_{mn} = \langle r^m(k) | H | r^n(k) \rangle = \langle r^m| P_k H P_k | r^n \rangle = \langle r^m| P_k H | r^n \rangle$$ 其中用到 $[P_k, H]=0$ 因为 $[T,H]=0$ ,和 $P_k^2=P_k$ 因为 $P_k$ 是投影算符。注意:这里所有计算过程中保持归一化。

所以计算这个新的哈密顿量块只需要把每组新基矢在哈密顿量作用下进行变换,再求这个基矢与 $k=0$ 时的那个基矢的内积就行了。因为 $|r^a(k=0)\rangle = |r^a\rangle$ 。

In [12]:
def state_cycle_shift(state: int, shift: int, total: int) -> int:
    """循环移位操作"""
    low = state >> (total - shift)
    mask = 2 ** (total - shift) - 1
    high = mask & state
    return (high << shift) + low

首先要从所有态里面把属于同一组的找出来

In [13]:
state_list_temp = state_list.copy()
state_k_all = []

while len(state_list_temp) != 0:
    state_k_list = []
    s_first = state_list_temp[0]
    s = s_first
    while True:
        state_k_list.append(s)
        state_list_temp.remove(s)
        s_next = state_cycle_shift(s, 1, N)
        if s_next == s_first:
            break
        else:
            s = s_next

    state_k_list.sort()
    state_k_all.append(state_k_list)

state_k_all
Out[13]:
[[3, 6, 9, 12], [5, 10]]

然后就可以构造哈密顿量了,构造哈密顿量的过程中需要计算哈密顿量与基矢的乘积,这里直接把基矢构造成矩阵,把最后一步整合成矩阵乘法了,否则的话要写嵌套4层循环才行。

In [14]:
k = 0
Hk = np.zeros((len(state_k_all),) * 2).astype(complex)

for new_b, b_list in enumerate(state_k_all):

    # build state_b
    state_new_b = np.zeros((1, len(state_list))).astype(complex)
    for rb, b in enumerate(b_list):
        ib = state_list.index(b)
        state_new_b[0, ib] = 1
    state_new_b = state_new_b / np.linalg.norm(state_new_b)

    for new_a, a_list in enumerate(state_k_all):
        state_new_a = np.zeros((len(state_list), 1)).astype(complex)

        # build state_a
        state_new_a_temp = np.zeros((len(state_list), 1)).astype(complex)
        for ra, a in enumerate(a_list):
            ia = state_list.index(a)
            state_new_a_temp[ia, 0] = np.exp(1j * 2 * np.pi / N * k * ra)
        state_new_a_temp = state_new_a_temp / np.linalg.norm(state_new_a_temp)

        # H @ state_a
        for ra, a in enumerate(a_list):
            ia = state_list.index(a)
            for i in range(N):
                j = (i + 1) % N
                ai = get_state_index(a, i)
                aj = get_state_index(a, j)

                # Sz
                new_ia = ia
                if ai == aj:
                    state_new_a[new_ia, 0] += state_new_a_temp[ia, 0]

                else:
                    state_new_a[new_ia, 0] -= state_new_a_temp[ia, 0]

                # SxSx + SySy
                if ai != aj:
                    b = flip_state(a, i)
                    b = flip_state(b, j)
                    new_ib = state_list.index(b)
                    state_new_a[new_ib, 0] += 1 / 2 * state_new_a_temp[ia, 0]
        # Hk
        Hk[new_a, new_b] = state_new_b @ state_new_a

这里构造得到的 $k=0$ 子空间只有 $2\times 2$ 维,从这里计算得到的也是基态。一般来说,基态总是在 $k=0$ 的子空间中。

In [15]:
np.linalg.eigvalsh(Hk)
Out[15]:
array([-4.44948974,  0.44948974])

利用对称性:其它离散对称性

除了总磁矩和平移对称性之外还有一些其它的离散对称性,例如对于 $k=0,\pi$ 时有宇称对称性和自旋翻转对称性等等。但是这些对称性与平移对称性并不对易,所以不能从平移对称性的结果中继续细分,而是要联合平移对称性一起去找基矢。

比如联合使用平移对称性和宇称对称性,要同时考虑平移算符和宇称算符来构造基矢,例如 $$ \mathcal{P}_k = \frac{1}{L} \sum_r^{L-1} C^\sigma\left( \frac{2\pi}{L} k r \right) T^r (1+P) $$ $$ C^\sigma\left( \frac{2\pi}{L} k r \right) = \left\lbrace \begin{array}{ll} \cos( \frac{2\pi}{L} k r), & p = 1 \\ \sin( \frac{2\pi}{L} k r), & p = -1 \end{array} \right. $$

程序待写

Fermi-Hubbard 模型

$$ H = -t \sum_{i,\sigma} \left( c^\dagger_{i,\sigma} c_{i+1,\sigma} + c^\dagger_{i+1,\sigma} c_{i,\sigma} \right) + U \sum_{i} n_{i\uparrow}n_{i\downarrow}$$

首先定义哈密顿量作用到基矢上的结果,基本原则就是如果作用有效果就返回作用后的基矢,如果作用为0就返回 None

In [16]:
from typing import Optional, Tuple


def check_U(state_up: int, state_down: int, i: int) -> Optional[Tuple[int, int]]:
    state_up_i = get_state_index(state_up, i)
    state_down_i = get_state_index(state_down, i)
    if state_up_i == state_down_i == 1:
        return state_up, state_down
    else:
        return None


def check_t(state: int, i: int, N: int) -> Optional[Tuple[int, int]]:
    j = (i + 1) % N
    state_i = get_state_index(state, i)
    state_j = get_state_index(state, j)
    if (state_i, state_j) == (0, 1) or (state_i, state_j) == (1, 0):
        state_new = flip_state(state, i)
        state_new = flip_state(state_new, j)
        return state_new
    else:
        return None

先实现一个全对角化,以此作为检查标准。

In [17]:
N = 4
t = 1
U = 1
H = np.zeros((4 ** N, 4 ** N))
al = []
for a_up in range(2 ** N):
    for a_down in range(2 ** N):
        a = (a_up << N) + a_down
        al.append(a)
        for i in range(N):
            # t
            b_up = check_t(a_up, i, N)
            if b_up is not None:
                b = (b_up << N) + a_down
                H[a, b] += -t

            b_down = check_t(a_down, i, N)
            if b_down is not None:
                b = (a_up << N) + b_down
                H[a, b] += -t

            # U
            b_result = check_U(a_up, a_down, i)
            if b_result is not None:
                b = (b_result[0] << N) + b_result[1]
                H[a, b] += U
In [18]:
# eigvals, eigvecs = eigsh(H, k=1, which='SA')
# eigvals
eigvals_all = np.linalg.eigvalsh(H)

首先利用粒子数对称性,这里考虑的是粒子数的分别守恒,总数守恒就把两组整数一起组合就行了。

这里取半满且两种自旋数目相同。

In [19]:
N = 4
Nup = 2
Ndown = 2
t = 1
U = 1

from itertools import combinations

state_up_list = [sum([1 << i for i in c]) for c in combinations(range(N), Nup)]
state_up_list.sort()
state_down_list = [sum([1 << i for i in c]) for c in combinations(range(N), Ndown)]
state_down_list.sort()
state_total_list = [(i, j) for i in state_up_list for j in state_down_list]
state_up_list, state_down_list
Out[19]:
([3, 5, 6, 9, 10, 12], [3, 5, 6, 9, 10, 12])

接下来找平移对称性,因为有两组基矢,所以平移的时候要同时对它们都操作。

In [20]:
state_list_temp = state_total_list.copy()
state_k_all = []

while len(state_list_temp) != 0:
    state_k_list = []
    s_first = state_list_temp[0]
    s = s_first
    while True:
        state_k_list.append(s)
        state_list_temp.remove(s)

        s0_next = state_cycle_shift(s[0], 1, N)
        s1_next = state_cycle_shift(s[1], 1, N)
        s_next = (s0_next, s1_next)

        if s_next == s_first:
            break
        else:
            s = s_next

    state_k_list.sort()
    state_k_all.append(state_k_list)

# state_k_all

接下来就构造哈密顿量。

这里取动量为$0$。

In [21]:
k = 0
Hk = np.zeros((len(state_k_all),) * 2).astype(complex)

for new_b, b_list in enumerate(state_k_all):

    # build state_b
    state_new_b = np.zeros((1, len(state_total_list))).astype(complex)
    for rb, b in enumerate(b_list):
        ib = state_total_list.index(b)
        state_new_b[0, ib] = 1
    state_new_b = state_new_b / np.linalg.norm(state_new_b)

    for new_a, a_list in enumerate(state_k_all):
        state_new_a = np.zeros((len(state_total_list), 1)).astype(complex)

        # build state_a
        state_new_a_temp = np.zeros((len(state_total_list), 1)).astype(complex)
        for ra, a in enumerate(a_list):
            ia = state_total_list.index(a)
            state_new_a_temp[ia, 0] = np.exp(1j * 2 * np.pi / N * k * ra)
        state_new_a_temp = state_new_a_temp / np.linalg.norm(state_new_a_temp)

        # H @ state_a
        for ra, a in enumerate(a_list):
            a_up = a[0]
            a_down = a[1]
            ia = state_total_list.index(a)
            for i in range(N):

                # t
                b_up = check_t(a_up, i, N)
                if b_up is not None:
                    b = (b_up, a_down)
                    new_ib = state_total_list.index(b)
                    state_new_a[new_ib, 0] += -t * state_new_a_temp[ia, 0]

                b_down = check_t(a_down, i, N)
                if b_down is not None:
                    b = (a_up, b_down)
                    new_ib = state_total_list.index(b)
                    state_new_a[new_ib, 0] += -t * state_new_a_temp[ia, 0]

                # U
                b_result = check_U(a_up, a_down, i)
                if b_result is not None:
                    b = b_result
                    new_ib = state_total_list.index(b)
                    state_new_a[new_ib, 0] += U * state_new_a_temp[ia, 0]
        # Hk
        Hk[new_a, new_b] = state_new_b @ state_new_a

# Hk

对角化一下新构造的哈密顿量,这个新哈密顿量的所有本征值都在全哈密顿量的谱里,这说明写对了。

从这个结果中我们知道,Fermi-Hubbard 模型的基态既是半满的,又是动量为$0$的。

In [22]:
eigvals = np.linalg.eigvalsh(Hk)

for x in eigvals:
    index = np.abs(eigvals_all - x).argmin()
    print(np.isclose(eigvals_all[index], x), eigvals_all[index], x)
True -4.723265519527195 -4.723265519527192
True 1.3836255242112377e-16 1.1102230246251525e-16
True 0.5058018686891442 0.5058018686891443
True 0.9999999999999989 0.999999999999999
True 0.9999999999999996 0.9999999999999996
True 1.0 1.0
True 1.0000000000000002 1.0000000000000002
True 1.4941981313108506 1.494198131310855
True 2.0 2.0
True 6.723265519527201 6.723265519527191

Gram-Schmidt Orthogonalization

Gram-Schmidt 正交化方法

把一组向量正交化的常用方法就是 Gram-Schmidt 正交化过程。 这个过程基于投影 $$ \mathrm{proj}_u(v) = \frac{\langle u,v \rangle}{\langle u , u \rangle} u $$ 每一次迭代得到一个与之前所有矢量都正交的新矢量 $$ \begin{array}{lll} b_1 &=& a_1 \\ b_2 &=& a_2 - \mathrm{proj}_{b_1}(a_2) \\ b_3 &=& a_3 - \mathrm{proj}_{b_1}(a_3) - \mathrm{proj}_{b_2}(a_3) \\ b_k &=& a_k - \sum_{i=1}^{k-1}\mathrm{proj}_{b_i}(a_k) \end{array} $$

In [1]:
import numpy as np
In [2]:
def proj(u, v, do_norm=False):
    if do_norm:
        result = np.vdot(u,v) / np.linalg.norm(u,u) * u
    else:
        result = np.vdot(u,v) * u
    return result.flatten()
In [3]:
def gram_schmidt(A):
    b = A.copy()
    b[:,0] = b[:,0] / np.linalg.norm(b[:,0])
    for i in range(1, A.shape[1]):
        b[:,i] = (b[:,i] 
                  - np.sum(np.apply_along_axis(
                      lambda u: proj(u, b[:,i]), 
                      0, 
                      b[:,:i]),
                           axis=1).flatten())
        b[:,i] = b[:,i] / np.linalg.norm(b[:,i])
    return b
In [4]:
a = np.random.rand(4,4)
b = gram_schmidt(a)
In [5]:
from itertools import combinations
for i,j in combinations(range(4),2):
    print(np.isclose(b[:,i] @ b[:,j], 0))
True
True
True
True
True
True
In [ ]:
 

Power Method for Eigenvalues

幂法:矩阵本征值迭代求解的基本方法

基本的幂法:求矩阵的最大本征值

$$ Ax^{(k)} = x^{(k+1)} $$

只要把任何一个初始随机向量反复地被一个矩阵作用,同时除以一个足够大的数保证不超过数值精度,那么最终的收敛结果就是矩阵的最大本征矢。

这种方法可行的原因是,矩阵可以看做是一个线性变换,对任何一个向量连续地作用同一个变换的结果,总是会让这个向量趋近于变换的主向量,也就是矩阵的最大本征值对应的本征矢。

In [1]:
import numpy as np
In [2]:
def power_iteration(A, num_simulations: int, v0=None):
    if v0 is None:
        v = np.random.rand(A.shape[1])
    else: 
        v = v0
        
    for _ in range(num_simulations):     
        v = A @ v / np.max(v)
    return v
In [3]:
a = np.random.rand(4,4)
e2,v2=np.linalg.eig(a)
ei2 = e2.argmax()
e2[ei2], v2[:,ei2] / np.linalg.norm(v2[:,ei2])
Out[3]:
((2.1973691032607885+0j),
 array([0.54317076+0.j, 0.56821252+0.j, 0.28769568+0.j, 0.54711174+0.j]))
In [4]:
v1 = power_iteration(a, 1000)
(a @ v1 / v1)[0], v1/ np.linalg.norm(v1)
Out[4]:
(2.197369103260788, array([0.54317076, 0.56821252, 0.28769568, 0.54711174]))

反幂法:求矩阵的最小本征矢

与幂法完全相同,只不过迭代的是矩阵的逆,显然矩阵的逆的最大本征值就是原矩阵的模最小的本征值。 $$ A^{-1}x^{(k)} = x^{(k+1)} $$ 不过由于矩阵求逆计算复杂度很大,可以把它转化成求线性方程组: $$ A x^{(k+1)} = x^{(k)} $$

In [5]:
def inverse_power_iteration(A, num_simulations: int, v0=None):
    if v0 is None:
        v = np.random.rand(A.shape[1])
    else: 
        v = v0
        
    for _ in range(num_simulations):  
        v = np.linalg.solve(A, v) 
        # v = np.linalg.inv(A) @ v
        v = v / np.max(v)
    return v
    
In [6]:
a = np.random.rand(4,4)
e2,v2=np.linalg.eig(a)
ei2 = np.abs(e2).argmin()
e2[ei2], v2[:,ei2] / np.linalg.norm(v2[:,ei2])
Out[6]:
(0.0934634074161824,
 array([ 0.06236468, -0.34577788,  0.87179586, -0.34135067]))
In [7]:
v1 = inverse_power_iteration(a, 1000)
1/ (np.linalg.inv(a) @ v1 / v1)[0], v1/ np.linalg.norm(v1)
Out[7]:
(0.09346340741618211,
 array([ 0.06236468, -0.34577788,  0.87179586, -0.34135067]))
In [ ]:
 

nikola: static site generator

Nikola: 基本用法

Nikola 是一个 python 写的静态博客生成器。参见官网getnikola 。主要的特色是原生支持 orgmode 和 ipynb 格式作为博客,看起来很方便。

备查资料

时间的格式

date +"%F %T UTC%:z"

博客模板

tags 用逗号分隔, category 被看做一个词。

orgmode

#+BEGIN_COMMENT
.. title: nikola: static site generator
.. slug: nikola-static-site-generator
.. date: 2020-12-19 15:19:12 UTC+08:00
.. tags: python, blog
.. category: tools
.. link: 
.. description: 
.. type: text
.. has_math: true

#+END_COMMENT

blog here.

ipynb

   "metadata": {
     "nikola": {
      "title": "Power Method for Eigenvalues",
      "slug": "power-method-for-eigenvalues",
      "date": "2020-12-19 15:20:52 UTC+08:00",
      "tags": "Numerical Algorithm,Linear Algebra",
      "category": "Algorithm",
      "link": "",
      "description": "",
      "type": "text"
     }
   }