跳转至

跳过符号微分

尽管符号微分适用于多数问题,但在以下情况下可能需要跳过符号微分:

  • 问题无法简单描述为符号表达式
  • 有其他更适合的自动微分方法
  • 函数需要调用编译好的外部库
  • 希望直接调用编译好的函数以提高速度

设置方法

如果需要跳过某个函数的符号微分,方法与使用缓存功能类似(参见编译速度过慢)。只需将手动编译的函数文件放置在指定的文件夹中,然后在 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 文件应包含向量函数 FGH 和变量 G_indexH_index_rowH_index_col。这些函数和变量的定义如下(也可参见 API 文档):

  • 函数 FGH 接收一维 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_indexH_index_rowH_index_col 是整数 NumPy 数组,用于标识 GH 返回的非零导数元素。H_index_rowH_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\)