分子动力学模拟之基于自动微分的LINCS约束
技术背景
在分子动力学模拟的过程中,考虑到运动过程实际上是遵守牛顿第二定律的。而牛顿第二定律告诉我们,粒子的动力学过程仅跟受到的力场有关系,但是在模拟的过程中,有一些参量我们是不希望他们被更新或者改变的,比如稳定的OH
键的键长就是一个不需要高频更新的参量。这时就需要在一次不加约束的更新迭代之后(如Velocity-Verlet算法等),再施加一次约束算法,重新调整更新的坐标,使得规定的键长不会产生较大幅度的变更。
初始化坐标参数
为了实现LINCS这一算法,我们先初始化一组随机的坐标用于测试,比如我们测试一个10原子的体系:
# constrain.py
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(0)
N = 10
crd = np.random.random((N, 3))
plt.figure()
plt.plot(crd[:,0], crd[:,1], 'o', color='black')
plt.savefig('initial.png')
初始化的体系效果如下,这是一个仅观测x-y平面的投影的结果(因为二维的投影在可视化上方便一些):
坐标的更新
参考牛顿定律,我们也用随机的方法产生一组初始速度,用于定义原子体系下一步的运动,再定义一个时间步长,我们就可以获取到下一步的体系坐标:
# constrain.py
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(0)
N = 10
crd = np.random.random((N, 3))
dt = 0.1
vel = np.random.random((N, 3))
new_crd = crd + vel * dt
plt.figure()
plt.plot(crd[:,0], crd[:,1], 'o', color='black')
plt.plot(new_crd[:,0], new_crd[:,1], 'o', color='red')
plt.savefig('move.png')
把旧的坐标和更新之后的坐标放到一起的可视化效果如下:
定义成键关系
因为LINCS约束是施加在键长这一相对参数上的,因此我们首先需要在测试的体系中定义一套成键的关系:
# constrain.py
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(0)
N = 10
crd = np.random.random((N, 3))
dt = 0.1
vel = np.random.random((N, 3))
new_crd = crd + vel * dt
# Add bonds information
bonds = np.array([[0,1],[0,2],[0,4],[2,3],
[2,4],[3,8],[5,8],[4,6],
[6,7],[7,9]])
plt.figure()
plt.plot(crd[:,0], crd[:,1], 'o', color='black')
plt.plot(new_crd[:,0], new_crd[:,1], 'o', color='red')
for bond in bonds:
plt.plot(crd[bond][:,0], crd[bond][:,1], color='green')
plt.plot(new_crd[bond][:, 0], new_crd[bond][:, 1], color='purple')
plt.savefig('move.png')
然后我们把成键关系也在可视化的结果中展现出来,得到这样一张图:
LINCS算法
接下来我们就讲到本文最核心的LINCS算法,其大致流程可以分为如下图(图片来自于参考链接1与LINCS原始文章)所示的3个步骤:
大致描述就是:先按照无约束的条件进行更新,这一点事实上我们在上一个章节中通过速度来更新坐标已经实现了这一操作。然后将更新后的成键在旧的成键上进行投影。最后对新的成键执行一个变换,即可得到保持原有键长的新的体系坐标。我们先看下相关的代码实现和结果,感兴趣的童鞋可以再往后阅读代码实现的思路和原理。
# constrain.py
import numpy as np
from jax import numpy as jnp
from jax import grad, jit, vmap
import matplotlib.pyplot as plt
# Initialization
np.random.seed(0)
N = 10
Dimension = 3
crd = np.random.random((N, Dimension))
# Mass diag
M = np.random.random(N)
Mi = np.identity(N) * M
Mii = np.identity(N) * (M ** (-1))
dt = 0.1
vel = np.random.random((N, Dimension))
new_crd = crd + vel * dt
# Add bonds information
bonds = np.array([[0,1],[0,2],[0,4],[2,3],
[2,4],[3,8],[5,8],[4,6],
[6,7],[7,9]])
# Bond length
di = np.linalg.norm(crd[bonds[:,0]] - crd[bonds[:,1]], axis=1)
# Automatic differentiation
def B(new_crd, bond, crd):
return jnp.linalg.norm(new_crd[bond[0]]-new_crd[bond[1]]) -\
jnp.linalg.norm(crd[bond[0]]-crd[bond[1]])
B_grad = grad(B, argnums=(0,))
B_vmap = jit(vmap(B_grad,(None,0,None)))
B_value = B_vmap(new_crd, bonds, crd)[0]
# LINCS
ccrd = new_crd.copy()
tmp0 = jnp.einsum('ij,kjl->kil', Mii, B_value)
tmp1 = jnp.einsum('jil,kil->jk', B_value, tmp0)
tmp2 = np.linalg.inv(tmp1)
tmp3 = jnp.einsum('ijk,jk->i', B_value, new_crd)-di
tmp4 = jnp.einsum('ij,j->i', tmp2, tmp3)
tmp5 = jnp.einsum('ijk,i->jk', B_value, tmp4)
tmp6 = jnp.einsum('ij,jk->ik', Mii, tmp5)
ccrd -= tmp6
# Draw
plt.subplot(211)
plt.plot(crd[:,0], crd[:,1], 'o', color='black')
plt.plot(new_crd[:,0], new_crd[:,1], 'o', color='blue')
plt.plot(ccrd[:,0], ccrd[:,1], 'o', color='red')
for bond in bonds:
plt.plot(crd[bond][:,0], crd[bond][:,1], color='black')
plt.plot(new_crd[bond][:,0], new_crd[bond][:,1], color='blue')
plt.plot(ccrd[bond][:, 0], ccrd[bond][:, 1], color='red')
plt.subplot(212)
di = np.linalg.norm(crd[bonds[:,0]] - crd[bonds[:,1]], axis=1)
diuc = np.linalg.norm(new_crd[bonds[:,0]] - new_crd[bonds[:,1]], axis=1)
dic = np.linalg.norm(ccrd[bonds[:,0]] - ccrd[bonds[:,1]], axis=1)
plt.plot(di, color='black')
plt.plot(diuc, color='blue')
plt.plot(dic, '+', color='red')
plt.savefig('move.png')
执行输出的结果如下图所示:
在这个结果中我们可以看到第二个图中红色的十字就是施加LINCS约束之后的结果,很显然的距离原始的键长更近。需要额外提醒的是,第一张图中的成键实际上是三维的成键,所以视觉上的大小差异不是真是的键长大小差异,具体差异数值还是以第二张图中展示的为准。
LINCS算法原理以及代码实现思路
首先我们提到了分子的动力学模拟过程还是遵守牛顿第二定律,也就是:
\]
其中\(\textbf r\)是一个\(N\times 3\)的三维坐标体系,这里\(N\)是体系的原子数,\(\textbf M\)是一个\(N\times N\)的对角矩阵,每一个对角元代表一个原子的质量。事实上在计算过程中更加经常用到的是\(\textbf M\)的逆矩阵,又由于\(\textbf M\)是一个对角矩阵,因此\(\textbf M^{-1}\)实际上就是每个对角元为对应原子质量的倒数这样的一个对角矩阵。\(\textbf f\)是跟\(\textbf r\)维度相同的体系作用力。
LINCS约束的方程可以表述为K个方程:
\]
其中K的大小在这里代表了成键的对数,简单理解就是保证每一对更新后的键的键长的大小与正常的键长大小保持一致,比方说固定了一个OH基中O和H的相对距离。施加该约束的过程可以表述为拉格朗日乘子法:
\]
其中非势能项可以定位为\(B^{T}\lambda\),其中\(B\)定义为:
\]
由于这个形式涉及到了微分,不过由于自动微分这项技术的诞生,使得我们不需要自己再去手动的计算这个微分项,只需要把\(g_i\)的形式给定,就可以在Jax中非常方便的计算其导数,并且有别于数值微分,自动微分兼具了高性能与高精度。而另外一点是向量化的操作,在Numba和Jax中分别支持了CPU上和GPU上的向量化操作,我们只需要写一条计算的方法,就可以把这个计算公式扩展到对更高维的数据进行处理,在Jax中这一功能接口为vmap。举个例子说,我们只需要写好计算\(B_i\)的过程,就可以直接用vmap推广到求整个的\(B\)。思路大体上就是如此,具体的过程可以参考上一章节中的源代码。
需要注意的是,这是一个0项,即一阶导数\(\frac{dg}{dt}\)和二阶导数\(\frac{d^2g}{dt^2}\)都是0的项,再结合leap-frog
坐标更新算法,可以得到最终的坐标更新表达式(具体的推导过程还是建议看下原始文章,很多平台比如Gromacs也是使用了最终的这个表达式来进行计算或者优化)为:
\]
我们从这个公式来分析下代码实现的流程,以及Python的实现过程中有可能遇到的一些坑。
注意事项一
\(\textbf r_{n+1}\)是基于\(\textbf r_{n+1}^{unc}\)来进行调整的,但是如果一开始直接使用:
r=r_unc
来初始化的话,会导致r_unc被覆盖,要知道r_unc还是会被频繁调用的,所以我们初始化的时候最好加上一个copy的操作。
注意事项二
矩阵乘法是从右往左来计算的,而Python中默认的矩阵乘法是从左往右的,因此最好不要直接使用Python中的乘号来直接计算多个矩阵的乘法,替代方案是手写numpy的multiply或者dot等函数配置参数。
注意事项三
在原始的论文中很多地方用到了求转置矩阵的操作,而面对高维矩阵的时候一定要指明操作所对应的轴,在本文的代码实现中,我们是使用了爱因斯坦求和
的操作,这个操作在numpy和jax中都有接口支持。
注意事项四
在原始的论文中,为了避免对矩阵进行求逆,使用了一些展开和截断的近似计算的技术。但是对于体系规模不大的场景,其实直接使用numpy或者jax中的求逆函数,速度也不会很慢,本文旨在算法的实现,这里就直接使用了jax的求逆函数。
注意事项五
在jax中的一些函数返回的结果是一个tuple的形式,这是使用vmap和jit技术经常会遇到的情况,虽然并不是很难处理,只需要在得到的结果上取一个0的index即可,但是在实际计算的过程中还是需要注意。
总结
具体的代码实现,都在上一个章节中完整的展示了出来,这一章节只是介绍了LINCS算法的形式以及实现LINCS算法的一些思路,更加详细的推导,还是建议看下原始论文。
总结概要
本文通过完整的案例及其算法实现的过程,介绍了LINCS(Linear Constraint Solver)这一分子动力学模拟过程常用的约束算法。得益于Jax这一框架的便用性及其对numpy的强大支持、对GPU计算的优化、还有自动微分与向量化运算等技术的实现,使得我们实现LINCS这一算法变的不再困难。
版权声明
本文首发链接为:https://www.cnblogs.com/dechinphy/p/lincs.html
作者ID:DechinPhy
更多原著文章请参考:https://www.cnblogs.com/dechinphy/
打赏专用链接:https://www.cnblogs.com/dechinphy/gallery/image/379634.html
腾讯云专栏同步:https://cloud.tencent.com/developer/column/91958
参考链接
分子动力学模拟之基于自动微分的LINCS约束的更多相关文章
- 分子动力学模拟之SETTLE约束算法
技术背景 在上一篇文章中,我们讨论了在分子动力学里面使用LINCS约束算法及其在具备自动微分能力的Jax框架下的代码实现.约束算法,在分子动力学模拟的过程中时常会使用到,用于固定一些既定的成键关系.例 ...
- Gromacs分子动力学模拟流程概述
Gromacs分子动力学模拟主要可以分为以下几个步骤,不同的体系步骤可能略有不同. 在开始之前,先简单了解一下预平衡: 分子动力学模拟的最终目的是对体系进行抽样,然后计算体系的能量,各种化学键,成分分 ...
- MindSpore:自动微分
MindSpore:自动微分 作为一款「全场景 AI 框架」,MindSpore 是人工智能解决方案的重要组成部分,与 TensorFlow.PyTorch.PaddlePaddle 等流行深度学习框 ...
- 附录D——自动微分(Autodiff)
本文介绍了五种微分方式,最后两种才是自动微分. 前两种方法求出了原函数对应的导函数,后三种方法只是求出了某一点的导数. 假设原函数是$f(x,y) = x^2y + y +2$,需要求其偏导数$\fr ...
- pytorch学习-AUTOGRAD: AUTOMATIC DIFFERENTIATION自动微分
参考:https://pytorch.org/tutorials/beginner/blitz/autograd_tutorial.html#sphx-glr-beginner-blitz-autog ...
- 自动微分(AD)学习笔记
1.自动微分(AD) 作者:李济深链接:https://www.zhihu.com/question/48356514/answer/125175491来源:知乎著作权归作者所有.商业转载请联系作者获 ...
- <转>如何用C++实现自动微分
作者:李瞬生转摘链接:https://www.zhihu.com/question/48356514/answer/123290631来源:知乎著作权归作者所有. 实现 AD 有两种方式,函数重载与代 ...
- 【转】C# winform 加载网页 模拟键盘输入自动接入访问网络
[转]C# winform 加载网页 模拟键盘输入自动接入访问网络 声明: 本文原创,首发于博客园 http://www.cnblogs.com/EasyInvoice/p/6070563.html ...
- (转)自动微分(Automatic Differentiation)简介——tensorflow核心原理
现代深度学习系统中(比如MXNet, TensorFlow等)都用到了一种技术——自动微分.在此之前,机器学习社区中很少发挥这个利器,一般都是用Backpropagation进行梯度求解,然后进行SG ...
随机推荐
- MySQL 中的各种锁机制
行级锁 行级锁是Mysql中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁. 行级锁能大大减少数据库操作的冲突.其加锁粒度最小,但加锁的开销也最大.行级锁分为共享锁和排他锁. 特点 开销大,加锁 ...
- TLS、SSL
TLS/SSL 的功能实现主要依赖于三类基本算法:散列函数 Hash.对称加密和非对称加密,其利用非对称加密实现身份认证和密钥协商,对称加密算法采用协商的密钥对数据加密,基于散列函数验证信息的完整性. ...
- Chapter 22 Target Trial Emulation
目录 22.1 The target trial 22.2 Causal effects in randomized trails 22.3 Causal effects in observation ...
- 编写Java程序,应用客户端和服务端通过 Eclipse 控制台的输入和显示实现简易的聊天功能
查看本章节 查看作业目录 需求说明: 应用客户端和服务端通过 Eclipse 控制台的输入和显示实现简易的聊天功能 实现思路: 创建 Java 项目,在项目中创建服务端类 ChatServerThre ...
- Kylin开启Kerberos安全认证
Kylin开启Kerberos安全认证, 由于Kylin是依赖Hbase启动的, Kylin启动脚本kylin.sh中就是调用的Hbase的启动脚本, 所以当Hbase开启了Keberos之后就等于K ...
- supervisor安装与基本使用
supervisor简介 一般的,我们部署一个项目,我们希望它能在挂了之后能自动重启,这时就要用守护进程了,而supervisor是用Python开发的一套通用的进程管理程序,能将一个普通的命令行进程 ...
- 关于PaddleSharp GPU使用 常见问题记录
在安装NVIDIA驱动过程中遇到一系列问题,记录如下! 附上PaddleSharp地址,大家喜欢可以关注 根据PaddleSharp官方说明需要安装驱动如下 CUDA 和 cuDNN 和 Tensor ...
- CVE-2021-45232 Apache APISIX 从未授权访问到RCE
00x1漏洞环境 Apache APISIX Dashboard 2.7 - 2.10 版本受到影响 通过git拉取在docker搭建环境 git clone https://github.com/a ...
- 一种适合于MC与SMC算法的哈希表设计
MC算法与SMC算法中的三角片焊接问题 在之前的关于MC算法与SMC算法的博文中介绍了算法的实现,文章主要围绕算法的核心问题,即三角片如何产生的问题进行了详细的描述.但由于实际应用中需要的等值面Mes ...
- 初识python 之 smtplib 发送(dolphinscheduler任务监测)邮件
需求 监测dolphinscheduler调度系统,任务执行异常情况.如有异常,则发送邮件通知. 处理思路 因DS本身自带的邮件发送功能,不能正常发送邮件. 故而,通过查询DS源数据表,获取当前任务执 ...