
简介一份聚焦生态动力学与非线性科学的学术资源面向生态系统动力学、非线性科学和生态建模领域的研究人员及研究生。该资源以四物种食物网为对象系统分析两种猎物、一种中间捕食者和一种顶级捕食者共存平衡点的稳定性并利用中心流形理论、分岔定理及数值模拟揭示霍普夫分岔、霍普夫-霍普夫分岔与倍周期分岔引发的极限环、准周期行为、混沌吸引子等复杂动态有助于深入理解物种共存与生态系统稳定性的关系。资源为单份PDF文档共1个文件压缩包大小3.75MB内容包含论文全文及详细数值模拟结果便于读者直接研读或引用。目前已有174人学习下载适合需要掌握食物网分岔分析方法、探索生态复杂性机制的科研人员使用。1. 四物种食物网模型多一个节点稳定性判断就换了套规则把三物种模型加一个节点事情就完全变样了。四物种食物网模型经常在同一组参数下于稳定点、周期振荡和混沌之间反复横跳这种动力学复杂性并不能用“物种越多越稳定”的直觉解释。这类模型要处理的是用微分方程描述资源、一级消费者、二级消费者和顶级捕食者之间的物质传递在参数空间里定位分岔边界再用雅可比矩阵特征值和最大李雅普诺夫指数回答“这个网络稳不稳”。它适合做种群建模、生态仿真和动力系统数值实验的人也是把模型跑起来时最容易踩坑的地方——积分器选取、步长控制、初值敏感任何一个都会让结论失真。2. 四物种食物网模型的方程建模与数值积分建模上来第一件事把“四物种”具体化成一条营养链。最常见的做法是资源 X1 → 一级消费者 X2 → 二级消费者 X3 → 顶级捕食者 X4每一层的捕食都写成 Holling II 型功能反应营养传递乘以转化效率每个消费者保留一个与食物无关的背景死亡率。写这道方程组时三物种里被默认忽略的细节会变成决定稳定性走向的关键项处理时间 h 越大高密度下的捕食越接近饱和系统越容易从发散振荡回到极限环转化效率 e 低到一定程度链会在对应节点上断掉四物种退化为三物种。dX1/dt r X1 (1 − X1/K) − a1 X1 X2 / (1 a1 h1 X1) dX2/dt e1 a1 X1 X2 / (1 a1 h1 X1) − m2 X2 − a2 X2 X3 / (1 a2 h2 X2) dX3/dt e2 a2 X2 X3 / (1 a2 h2 X2) − m3 X3 − a3 X3 X4 / (1 a3 h3 X3) dX4/dt e3 a3 X3 X4 / (1 a3 h3 X3) − m4 X42.1 为什么是链式四物种而不是任意网络这里写的是级联型结构上层只吃相邻下层。选型理由有两个。其一这是把三物种经典模型外推到四维最直接的路径后面算雅可比矩阵、做分岔扫描都有现成的解析结构可对照。其二级联型已经能产生四物种模型里最典型的动力学复杂性——准周期与混沌窗口不需要引入杂食、双向捕食这些额外拓扑变量。如果你想验证“拓扑结构本身对稳定性的贡献”把某一条边改成杂食边X4 同时吃 X2 和 X3再跑同一套流程即可方程与算法都不必改动。2.2 最小可跑模型与参数基准表参数基准我一般按“资源快、消费者慢、顶级捕食者最慢”的时间尺度来设。下表给出一组能让四物种共存并处在极限环附近的基准值也是后续所有分岔扫描的起点。参数含义基准值r资源内禀增长率1.2K资源环境容量50a1 / a2 / a3各级攻击率0.55 / 0.45 / 0.30h1 / h2 / h3处理时间0.4 / 0.3 / 0.5e1 / e2 / e3转化效率0.25 / 0.35 / 0.30m2 / m3 / m4消费者背景死亡率0.12 / 0.10 / 0.06调用 SciPy 的 solve_ivp 是四物种模型最省事的落地方案。完整最小实现如下。import numpy as np from scipy.integrate import solve_ivp def food_web_4(t, x, p): X1, X2, X3, X4 x # 三个功能反应项从低到高依次计算 f1 p[a1] * X1 / (1 p[a1] * p[h1] * X1) f2 p[a2] * X2 / (1 p[a2] * p[h2] * X2) f3 p[a3] * X3 / (1 p[a3] * p[h3] * X3) dX1 p[r] * X1 * (1 - X1 / p[K]) - f1 * X2 dX2 p[e1] * f1 * X2 - p[m2] * X2 - f2 * X3 dX3 p[e2] * f2 * X3 - p[m3] * X3 - f3 * X4 dX4 p[e3] * f3 * X4 - p[m4] * X4 return [dX1, dX2, dX3, dX4]参数用字典打包扫描时只改一个键状态向量顺序固定为 [X1, X2, X3, X4]。积分器的选择直接决定后续稳定性指标的可靠性。四物种系统的刚性区间比三物种宽RK45 在这种参数下往往要跑上万步LSODA 会在刚性区间自动切换隐式方法因此是默认首选。下面把基准参数和积分调用写到一起。p {r: 1.2, K: 50, a1: 0.55, h1: 0.4, e1: 0.25, m2: 0.12, a2: 0.45, h2: 0.3, e2: 0.35, m3: 0.10, a3: 0.30, h3: 0.5, e3: 0.30, m4: 0.06} sol solve_ivp(food_web_4, (0, 2000), [20, 8, 2, 0.5], args(p,), methodLSODA, rtol1e-6, atol1e-9, max_step1.0, dense_outputTrue)rtol 和 atol 压到 1e-6 / 1e-9 这个量级理由不是积分本身需要多高的精度而是后一步计算最大李雅普诺夫指数时扰动轨道的分离会对误差指数级放大先验值设得太松测到的“正指数”可能是数值噪声。max_step1.0 保证即使在高频振荡区输出也不会漏掉峰值。dense_outputTrue 让你在任何时刻 t 都能用 sol.sol(t) 取插值画相图时特别方便。2.3 数值积分的两条纪律第一个纪律是瞬态必须丢弃。四物种系统从任意初值出发头几百个时间单位都在朝吸引子收敛这段数据不能拿去统计峰值或算李雅普诺夫指数。实践中我会先跑 2000 个时间单位只取后 30% 做分析。第二个纪律是负生物量必须当作事故处理。solve_ivp 不会拦着变量变负负值一旦出现后面所有指标尤其特征值都会失真常见做法是把它们强行截到 0这在三物种模型里偶尔能糊弄过去在四物种模型里会人为制造一个“假平衡点”后面要花几倍时间排查。注意不要用 np.clip 把负生物量截成 0 再继续积分。正确做法是引入事件函数在变量触零时停止积分判定该参数组走向灭绝。两条纪律都落实了才谈得上做平衡点与特征值分析。3. 平衡点、雅可比矩阵与四物种稳定性判据稳定性分析的第一步不是算特征值而是先找到“代表生态系统共存”的内平衡点。四物种系统的平衡点方程是四个右端项同时等于零的代数方程组边界平衡点某一种群为 0总是存在但它们描述的是灭绝后的系统真正要判定的是四个物种都为正的可行内点。麻烦在于这样的内点可能不止一个。3.1 用 fsolve 找可行内点代数方程组是强非线性的解析解在 h 项存在时基本写不出来。我用 scipy.optimize.fsolve 配合多组初值去搜初值覆盖从“资源主导”到“顶级捕食者主导”的几种生物量格局。from scipy.optimize import fsolve def equilibrium(x, p): return np.array(food_web_4(0, x, p)) for guess in ([25, 10, 4, 1], [30, 8, 3, 0.8], [18, 12, 2, 0.4]): sol fsolve(equilibrium, guess, args(p,), xtol1e-10, maxfev10000) print(初值, guess, - 解, sol, 残差, np.max(np.abs(equilibrium(sol, p))))xtol 是根的近似误差控制。四物种系统种群的量级差异大取 1e-10 比较稳妥。残差必须打印出来fsolve 的非线性优化不一定收敛到根返回的解可能残差很大。最后保留所有分量大于 0 的解如果多组初值收敛到同一个正解说明这个参数区域只有一个可行内点。3.2 雅可比矩阵别手推交给 sympy四物种链的雅可比矩阵有 16 个偏导项手推容易在某个交叉项漏一个符号。这里让 sympy 做符号求导再数值代入——这也是后面做参数扫描时最有复用价值的一步。import sympy as sp X1, X2, X3, X4 sp.symbols(X1 X2 X3 X4, positiveTrue) r, K sp.symbols(r K, positiveTrue) a1, h1, e1, m2 sp.symbols(a1 h1 e1 m2, positiveTrue) a2, h2, e2, m3 sp.symbols(a2 h2 e2 m3, positiveTrue) a3, h3, e3, m4 sp.symbols(a3 h3 e3 m4, positiveTrue) F1 r*X1*(1 - X1/K) - a1*X1*X2/(1 a1*h1*X1) F2 e1*a1*X1*X2/(1 a1*h1*X1) - m2*X2 - a2*X2*X3/(1 a2*h2*X2) F3 e2*a2*X2*X3/(1 a2*h2*X2) - m3*X3 - a3*X3*X4/(1 a3*h3*X3) F4 e3*a3*X3*X4/(1 a3*h3*X3) - m4*X4 J sp.Matrix([[sp.diff(F1, X1), sp.diff(F1, X2), sp.diff(F1, X3), sp.diff(F1, X4)], [sp.diff(F2, X1), sp.diff(F2, X2), sp.diff(F2, X3), sp.diff(F2, X4)], [sp.diff(F3, X1), sp.diff(F3, X2), sp.diff(F3, X3), sp.diff(F3, X4)], [sp.diff(F4, X1), sp.diff(F4, X2), sp.diff(F4, X3), sp.diff(F4, X4)]]) def jacobian_numeric(x, p): subs {X1: x[0], X2: x[1], X3: x[2], X4: x[3], r: p[r], K: p[K], a1: p[a1], h1: p[h1], e1: p[e1], m2: p[m2], a2: p[a2], h2: p[h2], e2: p[e2], m3: p[m3], a3: p[a3], h3: p[h3], e3: p[e3], m4: p[m4]} return np.array(J.subs(subs), dtypefloat)数值雅可比函数把符号矩阵复用起来扫描不同参数时唯一要改的是输入 p。用这个函数算出内点处的雅可比矩阵再取特征值就得到局部稳定性判定所有特征值实部都为负内点局部渐近稳定存在一对共轭纯虚根而其余实部为负说明发生了霍普夫分岔系统正在把稳态交给周期振荡。3.3 把判据做成扫描函数特征值判定本身不是终点。四物种食物网模型的动力学复杂性来自“参数扫过去稳定性状态连续切换”所以判据要被包成一个可对参数网格循环的函数。判据计算方式稳定性结论谱横坐标 αα max Re(λ_i)α 0 局部渐近稳定4 阶 Routh-HurwitzΔ1、Δ2、Δ3、Δ4 全部大于 0实部全负的代数等价条件霍普夫条件一对共轭纯虚根分岔临界极限环开始出现跨零速度d(Re λ)/dp 在临界点取值决定分岔方向超临界还是亚临界Routh-Hurwitz 在四阶时有一组繁琐的连乘不等式手工算容易出错数值上直接用特征值的最大实部做判断即可。下面把资源增长率 r 作为扫描参数r_grid np.linspace(0.8, 2.2, 300) alpha [] x_guess [20.0, 8.0, 2.0, 0.5] for r_v in r_grid: p_local dict(p) p_local[r] r_v sol fsolve(equilibrium, x_guess, args(p_local,), xtol1e-10) if np.any(sol 0): alpha.append(np.nan) continue eig np.linalg.eigvals(jacobian_numeric(sol, p_local)) alpha.append(eig.real.max()) x_guess solalpha 数组从负变正的位置就是内点失稳的参数值。级联型四物种系统里这个位置绝大多数对应霍普夫分岔后面的极限环又会进一步把系统推向更复杂的动态——这正是下一章要量化的部分。4. 动力学复杂性分岔图与最大李雅普诺夫指数特征值判据只能说明平衡点何时失稳一旦系统进入周期振荡就要换工具描述动力学复杂性。四物种模型的常见现象是r 增大到某个值后极限环开始周期倍化——一个周期变成两个再变成四个随后进入混沌。这个过程在参数轴上呈现为一系列狭窄的窗口窗口内轨道永不重复但对初值极其敏感。4.1 用局部极值画分岔图要观察周期倍化路径最直接的办法是扫描某个参数在每个参数值下记录顶级捕食者 X4 的局部极大值。周期 1 对应一个点周期 2 对应两个点混沌对应一条连续带。import matplotlib.pyplot as plt from scipy.signal import argrelmax r_grid np.linspace(0.8, 2.6, 300) x0_current np.array([20.0, 8.0, 2.0, 0.5]) for r_v in r_grid: p[r] r_v sol solve_ivp(food_web_4, (0, 1600), x0_current, args(p,), methodLSODA, rtol1e-6, atol1e-9, max_step1.0) series sol.y[3] # 观测顶级捕食者 X4 mask sol.t 1200.0 # 丢弃瞬态只保留后 400 个时间单位 peaks argrelmax(series[mask], order5)[0] if len(peaks) 2: plt.scatter([r_v] * len(peaks), series[mask][peaks], s1) x0_current sol.y[:, -1] # 延续末状态作为下一组参数的初值 plt.xlabel(r) plt.ylabel(X4 peaks) plt.show()延续技巧是这里容易漏掉的一步把上一组参数的末状态作为下一组参数的初值轨道会更快落到吸引子上窄混沌窗口才不会被瞬态淹没。order5 是给峰检测一个最小跨度过滤数值小抖动。4.2 最大李雅普诺夫指数混沌的硬指标分岔图上的连续带可能来自混沌也可能来自周期很长的极限环最大李雅普诺夫指数 λ1 是区分二者的硬指标。计算上常用 Benettin 方法同时积分一条基准轨道和一条带微小扰动的轨道每隔固定时间把扰动方向拉回初始幅值累计每次拉伸的对数。def largest_lyapunov(f, x0, params, total200, dt1.0, eps1e-6): # 先烧掉瞬态 burn solve_ivp(f, (0, 200), x0, args(params,), methodLSODA, rtol1e-8, atol1e-10) x1 burn.y[:, -1] rng np.random.default_rng(3) x2 x1 eps * rng.standard_normal(4) lam_sum, n 0.0, 0 while total 0: s1 solve_ivp(f, (0, dt), x1, args(params,), methodLSODA, rtol1e-8, atol1e-10) s2 solve_ivp(f, (0, dt), x2, args(params,), methodLSODA, rtol1e-8, atol1e-10) x1, x2 s1.y[:, -1], s2.y[:, -1] d max(np.linalg.norm(x2 - x1), 1e-12) lam_sum np.log(d / eps) x2 x1 eps * (x2 - x1) / d # 拉回基准幅值保留方向 total - dt n 1 return lam_sum / (n * dt)这里的 eps 是扰动幅值不能取得太小否则会被浮点误差淹没dt 是重归一化间隔取 12 个时间单位。λ1 为正是混沌接近 0 是准周期为负则是稳定点或稳定极限环。需要说明的是四物种模型的混沌窗口通常很窄参数扫描步长必须小到 0.01 以下否则窗口会被直接跳过去。动态类型特征值表现最大李雅普诺夫指数稳定内点所有 Re(λ) 0λ1 0极限环一对共轭虚根λ1 ≈ 0准周期两对共轭虚根λ1 ≈ 0λ2 ≈ 0混沌—λ1 04.3 四物种的混沌窗口来自哪里把分岔图和 λ1 对着看会得到一个反直觉的结论第四物种不一定让系统“更复杂”它既可能打开混沌窗口也可能通过参数后移把窗口彻底关掉。这里的关键参数是顶级捕食者的死亡率 m4。m4 太大第 4 营养级直接灭绝系统退回三物种m4 太小顶级捕食者数量被压得很低链上的三级相互作用把周期倍化路径切断。只有 m4 落在中间一段窄区间时X4 的存在才会把二物种极限环的二维环面拉伸成准周期流形再在某个临界点折叠出混沌。实际项目里遇到“跑不出混沌”时多半不是建模错了而是扫描分辨率不够。先用延续技巧粗扫 r、e、m4 三个参数找到连续带出现的位置再在带内用步长 0.005 的细扫配合 λ1 复核才能确认四物种混沌窗口的真实边界。5. 稳定性验证灭绝事件与多初值断言模型建好、指标算完最后一步是证明这些结论不是数值积分制造的幻觉。这一步我固定做两件事。5.1 用事件函数挡住“假数据”负生物量会让特征值和李雅普诺夫指数全部失真却很容易被忽略。正确做法是把“任一种群降到 0”定义成事件触发后立即终止积分def extinct(t, x): return min(x) # 任一生物量触零 extinct.terminal True # 触发后停止积分 extinct.direction -1 # 只在下降沿触发 sol solve_ivp(food_web_4, (0, 2000), [20, 8, 2, 0.5], args(p,), eventsextinct, rtol1e-6, atol1e-9) if sol.t[-1] 2000: print(该参数组走向灭绝终止于 t , sol.t[-1])提示事件回调只接收 (t, y) 两个参数额外参数要用闭包或全局变量传入不要在事件函数里再写 args。5.2 单条轨迹不作数多初值断言四物种系统普遍存在多稳态同一组参数、不同初值会分别收敛到稳定内点和极限环。单条轨迹的结论在这个情况下没有代表性。常见的验证是做 2050 组随机初值把末状态聚在一起看分布rng np.random.default_rng(7) ends [] for _ in range(20): x0 rng.uniform(0.5, 30, size4) s solve_ivp(food_web_4, (0, 800), x0, args(p,), eventsextinct, rtol1e-6, atol1e-9) ends.append(s.y[:, -1]) ends np.array(ends)末状态聚成一个点说明该参数下的稳定吸引子是内点聚成离散的几个簇说明存在多稳态散得很开则要结合 λ1 判断是初值敏感还是混沌。对四物种食物网模型多稳态与混沌常常同时出现报告稳定性结论时必须同时给出参数和初值范围。事件终止加末态聚合三十秒内就能确认一组参数是否值得继续做细扫。本文还有配套的精品资源点击获取