自动求导, 道阻且长

Symars Rust代码生成库和 Raddy 自动求导库的来龙去脉

故事的起因

前段时间读了一些物理模拟的论文,想尝试复现一下。下手点先选了 stable neo hookean flesh simulation

这之中就涉及到了:对能量的本构模型求导数(一阶梯度,二阶 hessian 矩阵)。

Dynamic Deformables 这篇文章中可以看出推导这个公式就要花不少功夫,于是我搜了搜更多东西,尝试寻找一些其他的解决方法:

找到的资料中,前者有 MATLAB 或者 SymPy,后者有 PyTorch 等深度学习库,和更适合的 TinyAD

但是一个致命的问题来了:上述工具都在 C++ 的工具链上,而我不会 C++。

我只好换一门我比较熟悉的语言:Rust。这是一切罪恶的开始…

一条看起来简单的路

目前 Rust 还没有一个可以求二阶 hessian 的自动求导库。SymPy 目前还不能生成 Rust 代码(可以,但是有 bug)。

考虑实现难度我先选了后者:从 SymPy 表达式生成 Rust 代码。于是有了 Symars

再去走没走过的路

为了解决上述问题,我打算尝试原来放弃的那条路:自动求导。

正确的走路姿势

稀疏之路

针对稀疏矩阵单独实现了其 hessian 的组装过程:

impl Objective<4> for SpringEnergy {
type EvalArgs = f64; // restlength

fn eval(&self, variables: &advec<4, 4>, restlen: &Self::EvalArgs) -> Ad<4> {
let p1 = advec::<4, 2>::new(variables[0].clone(), variables[1].clone());
let p2 = advec::<4, 2>::new(variables[2].clone(), variables[3].clone());
let len = (p2 - p1).norm();
let e = make::val(0.5 * self.k) * (len - make::val(*restlen)).powi(2);
e
}
}

结语

收获: