- 金融科技
- 科学计算
【免费下载链接】tf-quant-finance
High-performance TensorFlow library for quantitative finance.
composite_scheme_step是 tf-quant-finance 有限差分 PDE 求解器中的"复合时间步进"构造器:它把两个独立的时间步进格式(time marching scheme)组合成一个单一的可调用对象,在求解过程中按已执行步数自动决定使用哪一个格式。本指南以该 API 文档为核心,结合仓库源码 composite_stepper.py、上层求解器 fd_solvers.py 以及官方测试 parabolic_equation_stepper_test.py,讲解其签名、参数语义、底层调用链,并给出可直接运行的组合方案示例。读完本文,你将掌握如何在自定义求解器中实现"先用格式 A 走 N 步、再用格式 B 走完剩余步数"的混合策略,以及它最典型的应用——振荡阻尼 Crank-Nicolson(Rannacher 风格)平滑。
API 总览:签名与作用
函数签名
composite_scheme_step定义于 composite_stepper.py,其完整签名如下:
tf_quant_finance.math.pde.steppers.composite_stepper.composite_scheme_step( first_scheme_steps, first_scheme, second_scheme )核心语义
该函数"组合两种时间步进格式":当已执行的步数小于first_scheme_steps时,使用first_scheme前进;否则使用second_scheme。换句话说,它允许在同一个求解过程中先执行一个"热身阶段"(如抑制振荡的高精度/高稳定格式),再切换到计算成本更低的格式完成剩余的演化。
tf_quant_finance.math.pde.steppers.composite_stepper.composite_scheme_step( first_scheme_steps, first_scheme, second_scheme )参数说明
| 参数 | 类型 | 含义 |
|---|---|---|
first_scheme_steps | Python 整数 | 应用first_scheme的步数。求解器每前进一步,内部计数器(num_steps_performed)递增;当num_steps_performed < first_scheme_steps时走first_scheme,否则走second_scheme。 |
first_scheme | 可调用对象(scheme) | 第一个时间步进格式,即parabolic_equation_step的time_marching_scheme参数所期望的格式(见下文)。 |
second_scheme | 可调用对象(scheme) | 第二个时间步进格式,规格同上。 |
返回值
返回一个可调用对象(step_fn),可直接作为有限差分 PDE 求解器中的one_step_fn使用(见 fd_solvers.py)。它遵循与crank_nicolson_step、extrapolation_step等完全相同的"stepper"契约:接收time、next_time、coord_grid、value_grid、boundary_conditions、各类系数回调以及num_steps_performed,返回更新后的(coord_grid, value_grid)二元组。
底层原理:从 step_fn 到 parabolic_equation_step 的调用链
composite_scheme_step返回的step_fn内部逻辑非常精简,核心代码如下(见 composite_stepper.py):
def step_fn(time, next_time, coord_grid, value_grid, boundary_conditions, second_order_coeff_fn, first_order_coeff_fn, zeroth_order_coeff_fn, inner_second_order_coeff_fn, inner_first_order_coeff_fn, num_steps_performed, dtype=None, name=None): name = name or 'composite_scheme_step' def scheme(*args, **kwargs): return tf.cond(num_steps_performed < first_scheme_steps, lambda: first_scheme(*args, **kwargs), lambda: second_scheme(*args, **kwargs)) return parabolic_equation_step( time, next_time, coord_grid, value_grid, boundary_conditions, second_order_coeff_fn, first_order_coeff_fn, zeroth_order_coeff_fn, inner_second_order_coeff_fn, inner_first_order_coeff_fn, time_marching_scheme=scheme, dtype=dtype, name=name)三个值得注意的实现细节
tf.cond实现运行时分支:格式选择不是 Python 层的一次性判断,而是通过tf.cond(num_steps_performed < first_scheme_steps, ...)嵌入计算图。这意味着num_steps_performed可以是图模式下的动态标量(例如tf.while_loop中的循环计数器),选择逻辑在每次步进时都生效,完全兼容tf.function/ graph 执行。num_steps_performed由上层求解器注入:该计数由fd_solvers._solve中的tf.while_loop维护。在 fd_solvers.py 的loop_body中,每次迭代都以num_steps_performed=steps_performed调用one_step_fn,并在循环返回时执行steps_performed + 1。因此first_scheme_steps是相对整个求解过程已执行步数的全局阈值,而不是相对本次 step_fn 调用的局部计数。与
start_step_count的配合:solve_backward/solve_forward都暴露了start_step_count参数(默认 0),用于声明"之前已经执行过的步数"。它直接作为tf.while_loop的初始steps_performed传入,因此当分多个片段求解(例如续算)时,composite_scheme_step的切换点会基于累计步数正确计算,而非每个片段各自从 0 重新计数。
底层parabolic_equation_step的契约
composite_scheme_step最终调用 parabolic_equation_step,它是求解形如
dV/dt + a * d2(A * V)/dx2 + b * d(B * V)/dx + c * V = 0的抛物型 PDE 单步推进器。传入的time_marching_scheme(此处即内嵌的scheme)必须按关键字接受:
value_grid:当前时刻的内部网格值(形状为value_grid[..., 1:-1]);t1、t2:步进前/后的时间;equation_params_fn:一个接收标量时间、构造三对角矩阵A(主、上、下三条对角线组成的元组)与非齐次项b的可调用对象。
parabolic_equation_step负责空间离散化(_construct_space_discretized_eqn_params,见 parabolic_equation_stepper.py)、边界条件处理(默认边界 / Robin 边界)以及步进后的边界值回填。因此,任何满足上述time_marching_scheme约定的格式(如crank_nicolson_scheme、extrapolation_scheme、implicit_scheme)都可以自由组合。
典型应用:振荡阻尼 Crank-Nicolson(oscillation_damped_crank_nicolson_step)
composite_scheme_step在仓库中最直接、最完整的落地实现是 oscillation_damped_crank_nicolson.py:
from tf_quant_finance.math.pde.steppers.composite_stepper import composite_scheme_step from tf_quant_finance.math.pde.steppers.crank_nicolson import crank_nicolson_scheme from tf_quant_finance.math.pde.steppers.extrapolation import extrapolation_scheme def oscillation_damped_crank_nicolson_step(extrapolation_steps=1): return composite_scheme_step(extrapolation_steps, extrapolation_scheme, crank_nicolson_scheme)为什么需要这种组合
Crank-Nicolson是二阶精度且无条件稳定的经典格式,其离散形式为
(u(t2) - u(t1)) / (t2 - t1) = (A(t1) u(t1) + b(t1) + A(t2) u(t2) + b(t2)) / 2见 crank_nicolson.py。但当初值/终值条件不光滑(例如 Heaviside 阶跃函数)时,它会在时间方向上产生高频振荡。
Extrapolation(外推)格式通过"两个隐式半步 + 一个完整隐式步"的组合消除振荡,代价是每个时间步的计算开销更高。其 docstring 明确说明(见 extrapolation.py):它对高波数分量的近似更好,因此在非光滑初值条件下不会出现 Crank-Nicolson 典型的振荡,但比 Crank-Nicolson 更慢,并建议考虑
oscillation_damped_crank_nicolson这一高效组合。组合策略:只在最前面
extrapolation_steps(默认 1)步使用 Extrapolation 格式"抹平"初值的不连续性,随后切回低成本的 Crank-Nicolson 完成剩余演化——既抑制振荡,又控制总计算成本。
测试中的实证验证
官方测试 parabolic_equation_stepper_test.py 的testCrankNicolsonOscillationDamping专门验证了这一效果:以 Heaviside 阶跃函数0.0 if x < 0 else 1.0作为初始条件求解热方程,参考解为1/2 + erf(x / (2 * sqrt(t))) / 2。测试注释明确指出:
"CrankNicolsonWithOscillationDamping produces much smaller error than the usual crank_nicolson_scheme."
即振荡阻尼版本相比纯 Crank-Nicolson 能显著降低误差。该测试在 1000 个网格点的均匀网格上、time_step=0.01条件下以error_tolerance=1e-3通过。
它还是默认求解器
值得注意:在 fd_solvers.py 中,当用户未显式传入one_step_fn时:
if one_step_fn is None: if n_dims == 1: one_step_fn = oscillation_damped_crank_nicolson_step() else: one_step_fn = douglas_adi_step(theta=0.5)即一维问题默认的one_step_fn就是复合方案(Extrapolation 1 步 + Crank-Nicolson)。因此,即使你不主动使用composite_scheme_step,只要调用pde.fd_solvers.solve_backward/solve_forward求解一维问题且不指定one_step_fn,底层已经在使用复合步进机制。
实战示例:在自定义求解中直接使用复合方案
下面给出一个完整的、可直接运行的示例框架(基于 fd_solvers.py 中欧式看涨期权定价示例改造),显式使用oscillation_damped_crank_nicolson_step作为one_step_fn:
import tensorflow.compat.v2 as tf import tf_quant_finance as tff pde = tff.math.pde num_grid_points = 1024 dtype = tf.float64 # 对数均匀网格 s_min = 0 s_max = 200 grid = pde.grids.uniform_grid( minimums=[s_min], maximums=[s_max], sizes=[num_grid_points], dtype=dtype) # 两笔不同行权价/波动率/利率的期权(batch=2) strike = tf.constant([[50], [100]], dtype) volatility = tf.constant([[0.3], [0.15]], dtype) rate = tf.constant([[0.01], [0.03]], dtype) expiry = 1.0 s = grid[0] final_value_grid = tf.nn.relu(s - strike) def second_order_coeff_fn(t, grid): del t return [[volatility**2 * grid[0]**2 / 2]] def first_order_coeff_fn(t, grid): del t return [rate * grid[0]] def zeroth_order_coeff_fn(t, grid): del t, grid return -rate @pde.boundary_conditions.dirichlet def lower_boundary_fn(t, grid): del t, grid return 0 @pde.boundary_conditions.dirichlet def upper_boundary_fn(t, grid): del grid return tf.squeeze(s_max - strike * tf.exp(-rate * (expiry - t))) # 显式指定复合 stepper:前 1 步用 Extrapolation,之后用 Crank-Nicolson one_step_fn = tff.math.pde.steppers.oscillation_damped_crank_nicolson.oscillation_damped_crank_nicolson_step() estimate = pde.fd_solvers.solve_backward( start_time=expiry, end_time=0, coord_grid=grid, values_grid=final_value_grid, time_step=0.01, one_step_fn=one_step_fn, boundary_conditions=[(lower_boundary_fn, upper_boundary_fn)], second_order_coeff_fn=second_order_coeff_fn, first_order_coeff_fn=first_order_coeff_fn, zeroth_order_coeff_fn=zeroth_order_coeff_fn, dtype=dtype)[0]要点说明:
- 求解是时间倒向的(从
start_time=expiry到end_time=0),对应金融中的终值条件定价;composite_scheme_step的步数计数与时间方向无关,只与已执行步数有关。 one_step_fn未指定时默认即为此复合方案,这里显式传入是为了演示如何在自定义求解中接管该参数。time_step与num_steps二者必须且只能指定其一(见 fd_solvers.py 的校验逻辑)。- 若要调整热身长度,可调用
oscillation_damped_crank_nicolson_step(extrapolation_steps=N),例如N=2表示先用 Extrapolation 走 2 步再切 Crank-Nicolson。
进阶:自定义两种格式的任意组合
composite_scheme_step本身并不关心两个 scheme 具体是什么,只要它们满足time_marching_scheme契约即可。例如,你可以:
from tf_quant_finance.math.pde.steppers.composite_stepper import composite_scheme_step from tf_quant_finance.math.pde.steppers.implicit import implicit_scheme from tf_quant_finance.math.pde.steppers.extrapolation import extrapolation_scheme # 先用隐式格式走 2 步(无振荡、一阶精度),再切外推格式(二阶精度) my_step = composite_scheme_step(2, implicit_scheme, extrapolation_scheme)设计建议(基于源码行为推断,非官方承诺):
first_scheme_steps一般取较小的正整数(1~3),因为它的目的通常是处理初始几个时间步内由不光滑初值引起的瞬态行为;取值过大会不必要地承担高性能格式的开销;- 若你需要"全程只用一个格式",直接使用该格式对应的
xxx_step()即可,无需套复合层; - 若你计划分段求解(如先粗后细),注意通过
solve_backward(..., start_step_count=...)传递累计步数,否则复合方案的切换点会从 0 重新计数,导致行为与预期不符。
相关模块与进一步阅读
- 复合步进实现:composite_stepper.py(模块文档见 composite_stepper.md)
- 振荡阻尼组合:oscillation_damped_crank_nicolson.py
- 单步推进器契约:parabolic_equation_stepper.py
- 高层求解入口(
solve_backward/solve_forward/_solve时间循环):fd_solvers.py - 求解器测试(含复合方案的对比验证):parabolic_equation_stepper_test.py
- 仓库中其他可组合的格式:Crank-Nicolson、Extrapolation、Implicit、Explicit、Weighted Implicit-Explicit(
theta=0.5时即 Crank-Nicolson,见 crank_nicolson.py),均可作为first_scheme/second_scheme的候选。
- 金融科技
- 科学计算
【免费下载链接】tf-quant-finance
High-performance TensorFlow library for quantitative finance.
相关推荐
tf-quant-finance 复合时间推进格式:composite_scheme_step 详解与组合使用指南
tf quant finance 复合时间推进格式:composite_scheme_step 详解与组合使用指南 导读 tf_quant_finance.ma
金融科技科学计算tf-quant-finance 显式时间推进格式(explicit_scheme)完全解析:PDE 有限差分求解中的显式步进
tf quant finance 显式时间推进格式(explicit_scheme)完全解析:PDE 有限差分求解中的显式步进 导读 本文围绕 tf quant
金融科技科学计算tf-quant-finance 中 Extrapolation 时间步进格式:PDE 求解器中抑制震荡的二阶精度方案
tf quant finance 中 Extrapolation 时间步进格式:PDE 求解器中抑制震荡的二阶精度方案 本篇文章聚焦 tf quant fina
金融科技科学计算
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考