跳过符号微分¶
尽管符号微分适用于多数问题,但在以下情况下可能需要跳过符号微分:
- 问题无法简单描述为符号表达式
- 有其他更适合的自动微分方法
- 函数需要调用编译好的外部库
- 希望直接调用编译好的函数以提高速度
设置方法¶
如果需要跳过某个函数的符号微分,方法与使用缓存功能类似(参见编译速度过慢)。只需将手动编译的函数文件放置在指定的文件夹中,然后在 set_* 函数中设置 cache 参数为该文件夹路径,pockit 将直接加载这些函数,从而实现跳过符号微分。
例如,若要替换第一个状态的动力学,可以准备 cache/dynamic_0.py,并向 set_dynamics 传入 cache="cache"。Pockit 会加载该文件,而不再从所给符号表达式生成函数。文件名如下:
| 函数 | 文件名 |
|---|---|
Phase.set_dynamics |
dynamic_{i}.py |
Phase.set_integral |
integral_{i}.py |
Phase.set_phase_constraint |
phase_constraint_{i}.py |
Phase.set_boundary_condition |
boundary_condition_0_{i}.py,boundary_condition_f_{i}.py,boundary_condition_t_0.py,boundary_condition_t_f.py |
System.set_objective |
objective.py |
System.set_system_constraint |
system_constraint_{i}.py |
文件格式¶
提供的 Python 文件应包含向量函数 F、G、H 和变量 G_index、H_index_row、H_index_col。这些函数和变量的定义如下(也可参见 API 文档):
- 函数
F、G和H接收一维 NumPy 数组x与整数l。对于标量函数 \(f(z_0,\ldots,z_{m-1})\),x按“参数优先”顺序保存l个求值点:
$$ x=[z_0^{(0)},\ldots,z_0^{(l-1)},z_1^{(0)},\ldots,z_{m-1}^{(l-1)}]. $$
因此,函数值满足
$$ F(x,l)j=f!\left(z_0^{(j)},\ldots,z\right), \qquad j=0,\ldots,l-1. $$}^{(j)
F返回长度为l的一维数组。G返回形状为(len(G_index), l)的数组,每一行对应一个已存储的梯度元素。H返回形状为(len(H_index_row), l)的数组,每一行对应一个已存储的 Hessian 元素。G_index、H_index_row和H_index_col是整数 NumPy 数组,用于标识G、H返回的非零导数元素。H_index_row和H_index_col长度相同,并且只包含 Hessian 的下三角索引,因为 Hessian 是对称矩阵。G_index必须严格递增;H_index_row必须单调不减;对于位于同一行的元素,H_index_col必须严格递增。
示例文件可参考生成的缓存文件。
设系统有 \(n_x\) 个状态变量、\(n_u\) 个控制变量、\(n_i\) 个积分和 \(n_s\) 个静态参数,则各函数的输入参数与维数如下:
| 函数 | 输入参数 | 输入维数 \(m\) |
|---|---|---|
Phase.set_dynamics |
\(x\)、\(u\)、\(t\)、\(s\) | \(n_x + n_u + 1 + n_s\) |
Phase.set_integral |
\(x\)、\(u\)、\(t\)、\(s\) | \(n_x + n_u + 1 + n_s\) |
Phase.set_phase_constraint |
\(x\)、\(u\)、\(t\)、\(s\) | \(n_x + n_u + 1 + n_s\) |
Phase.set_boundary_condition |
\(s\) | \(n_s\) |
System.set_objective |
\(I\)、\(s\) | \(n_i + n_s\) |
System.set_system_constraint |
\(I\)、\(s\) | \(n_i + n_s\) |