ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SymPy 力学表达式处理 API 深度解析:msubs 与 find_dynamicsymbols

SymPy 力学表达式处理 API 深度解析:msubs 与 find_dynamicsymbols SymPy 力学表达式处理 API 深度解析msubs 与 find_dynamicsymbols【免费下载链接】sympyA computer algebra system written in pure Python项目地址: https://gitcode.com/GitHub_Trending/sy/sympy本篇文章以 expr_manip.rst 为骨架深入剖析 SymPyphysics.mechanics模块中两个最常用的表达式处理工具用于批量替换动力学符号的msubs和用于收集表达式中所有时间相关符号的find_dynamicsymbols。读完本文你将掌握这两个 API 的完整参数语义、底层实现原理以及它们在多体动力学建模如 Kane 方法、拉格朗日方法中的典型应用方式。expr_manip.rst是physics.mechanicsAPI 文档树中的一页通过 Sphinx 的autofunction指令从源码 docstring 自动生成对应 sympy/physics/mechanics/functions.py 中的两个公开函数msubs与find_dynamicsymbols。它们与同目录下的 printing.rst、method.rst 等页面一起构成力学模块完整的 API 参考总入口见 index.rst。两个函数均已通过 sympy/physics/mechanics/init.py 的__all__导出用户可直接以from sympy.physics.mechanics import msubs, find_dynamicsymbols方式导入。背景动力学符号dynamicsymbols从何而来要理解这两个函数首先需要理解它们操作的对象——动力学符号dynamicsymbols。动力学符号是随时间变化的量在 SymPy 中被建模为关于时间t的未定义函数AppliedUndef。其定义位于 sympy/physics/vector/functions.pydef dynamicsymbols(names, level0, **assumptions): Uses symbols and Function for functions of time. Creates a SymPy UndefinedFunction, which is then initialized as a function of a variable, the default being Symbol(t). 关键设计点默认时间变量dynamicsymbols._t Symbol(t)见 sympy/physics/vector/functions.py所有动力学符号默认是Symbol(t)的函数。该类属性可以被重新赋值测试用例 test_functions.py 中演示了临时改用时再恢复的写法但正常建模中无需修改。level参数指定返回符号的求导阶数。dynamicsymbols(x, level1)等价于x.diff(t)内部通过reduce(diff, [t] * level, esses(t))实现。assumptions参数支持real、positive、commutative、integer等假设与symbols()的用法一致。例如dynamicsymbols(q, realTrue)返回的q(t)具有is_real True属性。在力学建模中广义坐标、广义速度、力、力矩等几乎都表示为动力学符号因此找出表达式里有哪些动力学符号find_dynamicsymbols和把动力学符号替换为数值或普通符号msubs成为两个高频需求这也是本页文档存在的意义。find_dynamicsymbols收集表达式中的全部动力学符号功能与参数find_dynamicsymbols返回表达式中出现的所有动力学符号集合set。完整签名functions.pydef find_dynamicsymbols(expression, excludeNone, reference_frameNone):参数类型含义注意事项expressionSymPy 表达式待分析的表达式可以是标量表达式也可以是Matrix、Vector当传入Vector时必须同时给出reference_frameexcludedynamicsymbols 的可迭代对象可选需要从结果中排除的动力学符号若传入非可迭代对象会抛出TypeErrorreference_frameReferenceFrame可选对向量表达式计算其动力学符号时参照的坐标系仅当expression为向量时需要否则抛出ValueError文档示例 from sympy.physics.mechanics import dynamicsymbols, find_dynamicsymbols from sympy.physics.mechanics import ReferenceFrame x, y dynamicsymbols(x, y) expr x x.diff()*y find_dynamicsymbols(expr) {x(t), y(t), Derivative(x(t), t)} find_dynamicsymbols(expr, exclude[x, y]) {Derivative(x(t), t)} a, b, c dynamicsymbols(a, b, c) A ReferenceFrame(A) v a * A.x b * A.y c * A.z find_dynamicsymbols(v, reference_frameA) {a(t), b(t), c(t)}注意第一个示例的输出集合中同时包含x(t)、y(t)以及导数Derivative(x(t), t)——求导后的表达式也被认为是动力学符号的一部分。借助exclude参数可以精确筛选出求导项之外或指定符号之外的剩余符号。底层实现原理从源码看find_dynamicsymbols的实现非常简洁functions.pyt_set {dynamicsymbols._t} if exclude: if iterable(exclude): exclude_set set(exclude) else: raise TypeError(exclude kwarg must be iterable) else: exclude_set set() if isinstance(expression, Vector): if reference_frame is None: raise ValueError(You must provide reference_frame when passing a vector expression, got %s. % reference_frame) else: expression expression.to_matrix(reference_frame) return {i for i in expression.atoms(AppliedUndef, Derivative) if i.free_symbols t_set} - exclude_set三个值得注意的实现事实识别依据是自由符号恰好只有时间 t函数先构建t_set {dynamicsymbols._t}然后对表达式调用atoms(AppliedUndef, Derivative)收集所有未定义函数节点和导数节点再用i.free_symbols t_set过滤。这样既不会漏掉Derivative(x(t), t)这样的导数节点也能把不依赖t的普通符号如常数符号a, b排除在外——测试用例 test_functions.py 验证了这一点普通symbols(a, b)不会出现在结果集中。对向量必须先投影到参考坐标系Vector本身没有可直接遍历的原子因此必须通过to_matrix(reference_frame)先转换成分量矩阵再分析若此时reference_frame为None会抛出ValueError对应测试 test_functions.py 中的raises(ValueError, ...)断言。exclude的类型校验非可迭代的exclude会直接抛TypeError这是对误用find_dynamicsymbols(expr, excludex)忘记用列表包裹单个符号的一层保护。msubs面向力学表达式的自定义替换为什么不能直接用subs力学表达式尤其是运动方程中充满Derivative节点而 SymPy 默认的expr.subs()会沿着表达式树深入Derivative内部进行替换这在很多时候不是用户期望的行为。msubs正是为此设计它只做一次树遍历且完全忽略Derivative内部的项。文档中的第一个示例最直观地说明了这一差异 from sympy.physics.mechanics import dynamicsymbols, msubs x dynamicsymbols(x) msubs(x.diff() x, {x: 1}) Derivative(x(t), t) 1x被替换成1但Derivative(x(t), t)保持不变——因为替换被刻意挡在Derivative节点之外。多字典替换msubs支持一次传入多个替换字典内部通过dict_merge(*sub_dicts)合并functions.py。后面的字典会覆盖前面字典中的重复键 x, y, z dynamicsymbols(x, y, z) sub1 {x: 1, y: 2} sub2 {z: 3, x.diff(): 4} msubs(x.diff() x y z, sub1, sub2) 10这里x.diff()被sub2中的4替换最终结果为4 1 2 3 10。注意x.diff()作为独立节点出现在表达式树中时是可以被替换的被忽略的只是替换项是导数内部的自变量这种情况。smartTrue规避 nan 的智能替换普通替换在遇到形如sin(x)/tan(x)在x0处求值时会产生nan0/0但该表达式经过化简后实际趋近于 1。msubs(..., smartTrue)正是为了处理这类数值替换时的病态情形 from sympy import sin, tan (sin(x)/tan(x)).subs(x, 0) nan msubs(sin(x)/tan(x), {x: 0}, smartTrue) 1docstring 中说明了其工作原理functions.py它首先把所有tan替换为sin/cos然后遍历每个节点。如果节点是分数先对分母求值若分母为 0则尝试对整个分数做化简。通过这种选择性化简只针对会产生 1/0 的子表达式从而获得更快的性能。对 Matrix / Vector / Dyadic 的内建支持msubs对力学模块的三种复合数据类型做了特殊处理functions.pyif isinstance(expr, (Matrix, Vector, Dyadic)): return expr.applyfunc(lambda x: func(x, sub_dict))即对矩阵、向量、并矢分别对其分量逐个应用替换。测试 test_functions.py 中验证了向量v x*N.x y*N.y和并矢d x*(N.x|N.x) y*(N.y|N.y)在{x: 0, y: 1}下分别得到1*N.y与1*(N.y|N.y)的预期结果。此外若表达式本身实现了msubs方法hasattr(expr, msubs)则优先调用其自身实现functions.py为用户自定义类型预留了扩展点。深入源码msubs 的四个内部助手msubs之所以能做到单次遍历 忽略导数内部依赖 functions.py 中的一组内部函数理解它们可以帮你更精准地预判msubs的行为_crawl(expr, func, *args, **kwargs)第 537 行通用的表达式树爬取器。先对当前节点调用func若返回None则递归处理所有子节点并用expr.func(*new_args)重建若返回非None则直接作为结果从而实现命中即短路。_sub_func(expr, sub_dict)第 546 行核心替换判定逻辑。def _sub_func(expr, sub_dict): Perform direct matching substitution, ignoring derivatives. if expr in sub_dict: return sub_dict[expr] elif not expr.args or expr.is_Derivative: return expr可以看到expr.is_Derivative是停止下钻的关键条件——导数节点本身若在sub_dict中仍可被替换但其内部的x(t)不会再被触碰。_tan_repl_func(expr)第 554 行smartTrue的第一趟遍历使用将tan节点改写为sin(*expr.args) / cos(*expr.args)为后续分母判零做准备。_smart_subs(expr, sub_dict)第 562 行smartTrue时使用的两趟遍历实现。第二趟递归中先通过_fraction_decomp(expr)把表达式拆成分子/分母第 601 行识别Mul中指数为负的Pow项归入分母若分母不为 1先递归求解分母若denom_subbed.evalf() 0则对整个表达式调用simplify(expr)消除病态因子否则正常返回num_subbed / denom_subbed。这样化简只发生在确实会除零的局部节点避免了全树化简带来的性能损耗。测试与使用佐证单元测试集中在 sympy/physics/mechanics/tests/test_functions.pytest_msubs()第 124 行覆盖矩阵元素的普通替换、smartTrue下cos(xy)*tan(xy)在x0, ypi/2处的结果b 1、以及 Vector/Dyadic 的替换。test_find_dynamicsymbols()第 148 行覆盖矩阵表达式的符号收集、exclude过滤、向量配合reference_frame使用、以及缺失reference_frame时的ValueError。此外msubs还被广泛用于 Kane 方程相关的测试如 test_kane.py、test_kane2.py用于在推导出符号形式的运动方程后代入数值验证结果这正是它在真实工作流中的典型定位find_dynamicsymbols负责看清方程依赖哪些变量msubs负责把抽象方程变成数值可算的形式。在 sympy/physics/mechanics/system.py、kane.py、lagrange.py 等核心模块中dynamicsymbols._t也被大量用于对广义坐标、约束方程求时间导数形成动力学符号 — 求导 — 替换求值的完整链条。小结两个 API 的协作范式在 SymPyphysics.mechanics的典型建模流程中这两个函数通常配合使用用dynamicsymbols声明广义坐标q1, q2, ...与广义速度u1, u2, ...通过KanesMethod/LagrangesMethod推导出符号形式的运动方程其中含大量Derivative节点用find_dynamicsymbols检查方程中剩余的自由动力学符号确认状态变量是否齐全用msubs将符号替换为具体数值或传入多组字典分步替换并在必要时开启smartTrue规避除零问题得到可数值求解的方程。理解find_dynamicsymbols基于free_symbols {t}的识别规则、msubs对Derivative节点内部不替换与自身可替换的双重语义以及在smartTrue下先化tan再判分母为零的化简策略就能在建模、仿真与调试中精准驾驭这两个 API。更多细节可直接阅读源码 sympy/physics/mechanics/functions.py 与其配套测试 test_functions.py。【免费下载链接】sympyA computer algebra system written in pure Python项目地址: https://gitcode.com/GitHub_Trending/sy/sympy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表