更新日志
本文档记录 PyNetIM 项目的所有重要变更。
格式基于 Keep a Changelog。
[0.4.5] - 2026-04-04
新增
SI 和 SIR 扩散模型 (C++ 后端):
新增
SusceptibleInfectedModel- SI 传播模型新增
SusceptibleInfectedRecoveredModel- SIR 传播模型支持单次模拟和 Monte Carlo 多轮模拟
支持多线程并行模拟
支持激活节点记录和激活频率记录
Graph 构造函数支持统一权重:
IMGraphCpp(num_nodes, edges, weights=1.0)- 默认所有边权重为 1.0IMGraphCpp(num_nodes, edges, 0.3)- 所有边使用统一权重 0.3IMGraphCpp(num_nodes, edges, [0.1, 0.2, ...])- 每条边使用各自的权重
API 更新
SusceptibleInfectedModel:
构造函数:
SusceptibleInfectedModel(graph, seeds, beta=0.1, max_steps=100, record_activated=False, record_activation_frequency=False)set_beta(beta: float)- 设置感染概率set_max_steps(max_steps: int)- 设置最大传播步数run_single_simulation(seed=None) -> int- 单次模拟run_monte_carlo_diffusion(rounds, seed=None, use_multithread=False, num_threads=0) -> float- Monte Carlo 模拟get_activated_nodes() -> Set[int]- 获取激活节点集合get_activation_frequency() -> List[int]- 获取激活频率
SusceptibleInfectedRecoveredModel:
构造函数:
SusceptibleInfectedRecoveredModel(graph, seeds, beta=0.1, gamma=0.1, record_activated=False, record_activation_frequency=False)set_beta(beta: float)- 设置感染概率set_gamma(gamma: float)- 设置恢复概率run_single_simulation(seed=None) -> int- 单次模拟run_monte_carlo_diffusion(rounds, seed=None, use_multithread=False, num_threads=0) -> float- Monte Carlo 模拟get_activated_nodes() -> Set[int]- 获取激活节点集合get_activation_frequency() -> List[int]- 获取激活频率
IMGraphCpp:
weights参数现在支持float类型,表示所有边使用统一权重
代码重构
扩散模型文件分离:
将扩散模型从单一文件分离到独立文件
independent_cascade.h- IC 模型linear_threshold.h- LT 模型susceptible_infected.h- SI 模型susceptible_infected_recovered.h- SIR 模型common.h- 共享代码(ObjectPool、RNG 工具函数)
测试
新增完整测试覆盖:
test_ic_model.py- IC 模型完整测试test_lt_model.py- LT 模型完整测试test_si_model.py- SI 模型完整测试test_sir_model.py- SIR 模型完整测试单线程和多线程结果一致性验证通过
参数验证测试通过(beta、gamma 范围检查)
技术细节
修改文件:
src/pynetim/cpp/include/common.h- 共享工具代码src/pynetim/cpp/include/independent_cascade.h- IC 模型src/pynetim/cpp/include/linear_threshold.h- LT 模型src/pynetim/cpp/include/susceptible_infected.h- SI 模型src/pynetim/cpp/include/susceptible_infected_recovered.h- SIR 模型src/pynetim/cpp/include/diffusion_model.h- 主头文件src/pynetim/cpp/bindings/si_bind.cpp- SI 模型绑定src/pynetim/cpp/bindings/sir_bind.cpp- SIR 模型绑定src/pynetim/cpp/include/Graph.h- Graph 类统一权重支持src/pynetim/cpp/bindings/graph_bind.cpp- Graph 绑定更新
实现细节:
SI 模型:每个时间步,感染节点以概率 beta 尝试感染邻居
SIR 模型:在 SI 基础上,感染节点每步以概率 gamma 恢复
参数验证:beta、gamma ∈ (0, 1],max_steps > 0
多线程种子预生成策略确保单线程和多线程结果完全一致
使用示例
import pynetim.cpp.graph as im_graph
import pynetim.cpp.diffusion_model as diffusion_model
# 创建图(统一权重)
graph = im_graph.IMGraphCpp(100, [(i, i+1) for i in range(99)], 0.3)
seeds = {0}
# SI 模型
si = diffusion_model.SusceptibleInfectedModel(graph, seeds, beta=0.3)
avg = si.run_monte_carlo_diffusion(1000, seed=42)
print(f"SI 平均感染节点数: {avg:.2f}")
# SIR 模型
sir = diffusion_model.SusceptibleInfectedRecoveredModel(graph, seeds, beta=0.3, gamma=0.1)
avg = sir.run_monte_carlo_diffusion(1000, seed=42)
print(f"SIR 平均感染节点数: {avg:.2f}")
# 多线程模拟
avg = sir.run_monte_carlo_diffusion(10000, seed=42, use_multithread=True)
print(f"多线程 SIR 平均感染节点数: {avg:.2f}")
# 记录激活节点
sir = diffusion_model.SusceptibleInfectedRecoveredModel(
graph, seeds, beta=0.3, gamma=0.1, record_activated=True
)
count = sir.run_single_simulation(seed=42)
activated = sir.get_activated_nodes()
print(f"激活节点: {activated}")
[0.4.4] - 2026-03-30
新增
分离模拟执行和结果获取:
run_single_simulation()现在只返回激活节点数量(int),不再返回节点集合新增
get_activated_nodes()方法,专门用于获取记录的激活节点集合只有在
record_activated=True时,get_activated_nodes()才返回有效数据当
record_activated=False时,get_activated_nodes()返回空集合
多轮模拟记录激活节点并集:
run_monte_carlo_diffusion()现在支持记录所有试验的激活节点并集当
record_activated=True时,get_activated_nodes()返回所有试验中被激活过的节点(并集)对于单次模拟:返回该次模拟的激活节点集合
对于 Monte Carlo 模拟:返回所有试验的激活节点并集
激活频率记录功能:
新增
record_activation_frequency参数(默认为false),用于控制是否记录每个节点的激活频率新增
set_record_activation_frequency(record)方法,可在运行时动态开启/关闭记录功能新增
get_activation_frequency()方法,返回每个节点被激活的次数只有在
record_activation_frequency=True时,get_activation_frequency()才返回有效数据当
record_activation_frequency=False时,get_activation_frequency()返回全 0 的列表可以同时开启
record_activated和record_activation_frequency,记录两种数据
API 更新
IndependentCascadeModel:
run_single_simulation()的返回值从Set[int]改为 ``int``(激活节点数量)新增
get_activated_nodes() -> Set[int]方法,用于获取记录的激活节点集合新增
get_activation_frequency() -> List[int]方法,用于获取每个节点的激活频率新增
set_record_activation_frequency(record: bool)方法,用于动态开启/关闭激活频率记录run_single_simulation()的seed参数类型从int改为int | None,默认值从0改为Nonerun_monte_carlo_diffusion()的seed参数类型从int改为int | None,默认值从0改为None构造函数新增
record_activation_frequency参数(默认为false)
LinearThresholdModel:
run_single_simulation()的返回值从Set[int]改为 ``int``(激活节点数量)新增
get_activated_nodes() -> Set[int]方法,用于获取记录的激活节点集合新增
get_activation_frequency() -> List[int]方法,用于获取每个节点的激活频率新增
set_record_activation_frequency(record: bool)方法,用于动态开启/关闭激活频率记录run_single_simulation()的seed参数类型从int改为int | None,默认值从0改为Nonerun_monte_carlo_diffusion()的seed参数类型从int改为int | None,默认值从0改为None构造函数新增
record_activation_frequency参数(默认为false)
优化
改进随机种子生成机制:
run_single_simulation()和run_monte_carlo_diffusion()方法现在都支持seed=None参数当
seed=None时,使用多个std::random_device值的组合来增强随机性当
seed=固定值时,使用固定种子,确保模拟结果可重现改进后的随机种子生成方式解决了在某些平台(虚拟环境、容器)上
std::random_device返回相同值的问题使用
std::seed_seq和多个随机值组合,确保每次调用都能得到不同的结果
多线程随机种子一致性:
run_monte_carlo_diffusion()采用种子预生成策略在开始任何模拟之前,使用主随机数生成器预生成所有试验的种子
每个试验使用独立的种子,确保单线程和多线程结果完全一致
多线程只是改变了试验的执行顺序和并行度,但没有改变每个试验使用的随机数序列
修复
修复 ``set_record_activated()`` 的清空逻辑:
当
set_record_activated(False)被调用时,现在会清空last_activated_nodes避免了返回过期的激活节点数据
确保当
record_activated=False时,get_activated_nodes()总是返回空集合
修复 ``set_record_activation_frequency()`` 的清空逻辑:
当
set_record_activation_frequency(False)被调用时,现在会清空activation_frequency避免了返回过期的激活频率数据
确保当
record_activation_frequency=False时,get_activation_frequency()总是返回全 0 的列表
测试
新增功能验证测试:
IC 模型单次模拟测试(简单链式图、星形图、低权重图)
LT 模型单次模拟测试
多个种子节点测试
record_activated=False测试set_record_activated()动态切换测试多次调用
get_activated_nodes()测试无向图测试
随机种子功能测试
多轮模拟测试(单线程和多线程)
大规模模拟测试(10000 轮)
record_activated对多轮模拟性能影响测试多轮模拟 vs 单次模拟对比测试
编译和运行测试全部通过
测试结果:
简单链式图 (权重=1.0): 激活节点数量=5, 激活节点集合={0, 1, 2, 3, 4} ✅
星形图 (权重=1.0): 激活节点数量=5, 激活节点集合={0, 1, 2, 3, 4} ✅
低权重图 (权重=0.01): 激活节点数量=1, 激活节点集合={0} ✅
多个种子节点: 激活节点数量=6, 激活节点集合={0, 1, 2, 3, 4, 5} ✅
record_activated=False: 返回激活节点数量,不记录节点集合 ✅
record_activated=True: 返回激活节点数量,记录节点集合 ✅
动态切换 record_activated: 正确清空和记录节点集合 ✅
多次调用 get_activated_nodes(): 返回相同的集合 ✅
无向图: 正确处理无向图的传播 ✅
随机种子 (seed=None): 每次模拟结果可能不同 ✅
固定种子 (seed=42): 多次模拟结果完全相同 ✅
单线程和多线程: 多轮模拟结果完全一致(差异 < 1e-10)✅
固定种子可重现性: 3次运行结果完全相同 ✅
record_activated 影响: 不影响多轮模拟结果(差异 = 0.0000)✅
大规模模拟: 10000 轮模拟稳定运行 ✅
技术细节
修改文件:
src/pynetim/cpp/include/diffusion_model.h- 添加核心功能实现、改进随机种子生成、修复清空逻辑src/pynetim/cpp/bindings/ic_bind.cpp- IC 模型 Python 绑定,更新 API 文档src/pynetim/cpp/bindings/lt_bind.cpp- LT 模型 Python 绑定,更新 API 文档src/pynetim/cpp/diffusion_model/independent_cascade_model.pyi- 类型提示src/pynetim/cpp/diffusion_model/linear_threshold_model.pyi- 类型提示
实现细节:
run_single_trial方法新增activated_nodes参数,用于记录激活节点添加
last_activated_nodes成员变量(mutable std::set<int>),存储最后一次模拟的激活节点当
record_activated为true时,遍历所有节点收集激活的节点到last_activated_nodes当
record_activated为false时,不收集激活节点,last_activated_nodes保持为空run_single_simulation()返回激活节点数量(int),不再返回节点集合新增
get_activated_nodes()方法,返回last_activated_nodes的副本set_record_activated()方法在设置为false时清空last_activated_nodes使用独立的随机数生成器确保每次模拟的独立性
保持向后兼容性,
record_activated默认为false随机种子生成改进: 使用多个
std::random_device值和std::seed_seq组合多线程种子预生成策略: 在开始任何模拟之前,使用主随机数生成器预生成所有试验的种子
Python 绑定层检测
seed参数是否为None,自动转换为use_random_seed标志record_activated参数不影响run_monte_carlo_diffusion()的性能和结果多线程模式下,每个线程使用独立的随机数生成器,确保线程安全和结果一致性
使用示例
# IC 模型
from pynetim.cpp.diffusion_model import IndependentCascadeModel
# 不记录激活节点(默认)
ic_model = IndependentCascadeModel(graph, seeds)
count = ic_model.run_single_simulation() # 随机种子
print(f"激活节点数量: {count}")
# 记录激活节点
ic_model = IndependentCascadeModel(graph, seeds, record_activated=True)
count = ic_model.run_single_simulation(seed=42) # 固定种子
activated_nodes = ic_model.get_activated_nodes()
print(f"激活节点数量: {count}")
print(f"激活的节点: {activated_nodes}")
# 动态切换记录
ic_model.set_record_activated(True)
count = ic_model.run_single_simulation()
activated_nodes = ic_model.get_activated_nodes()
ic_model.set_record_activated(False)
count = ic_model.run_single_simulation()
activated_nodes = ic_model.get_activated_nodes() # 空集合
# LT 模型
from pynetim.cpp.diffusion_model import LinearThresholdModel
lt_model = LinearThresholdModel(graph, seeds, theta_l=0.0, theta_h=1.0, record_activated=True)
count = lt_model.run_single_simulation() # 随机种子
activated_nodes = lt_model.get_activated_nodes()
print(f"激活节点数量: {count}")
print(f"激活的节点: {activated_nodes}")
# 多轮模拟
avg_count = ic_model.run_monte_carlo_diffusion(1000) # 随机种子
print(f"平均激活节点数: {avg_count:.2f}")
avg_count = ic_model.run_monte_carlo_diffusion(1000, seed=42) # 固定种子
print(f"平均激活节点数: {avg_count:.2f}")
# 多线程多轮模拟
avg_count = ic_model.run_monte_carlo_diffusion(1000, seed=42, use_multithread=True, num_threads=4)
print(f"平均激活节点数: {avg_count:.2f}")
# 激活频率记录
ic_model = IndependentCascadeModel(graph, seeds, record_activation_frequency=True)
avg_count = ic_model.run_monte_carlo_diffusion(1000, seed=42)
freq = ic_model.get_activation_frequency()
print(f"节点激活频率: {freq}")
# 同时记录激活节点和频率
ic_model = IndependentCascadeModel(graph, seeds, record_activated=True, record_activation_frequency=True)
avg_count = ic_model.run_monte_carlo_diffusion(1000, seed=42)
activated_nodes = ic_model.get_activated_nodes() # 所有试验的激活节点并集
freq = ic_model.get_activation_frequency() # 每个节点的激活次数
print(f"所有试验中被激活过的节点: {activated_nodes}")
print(f"节点激活频率: {freq}")
[0.4.3] - 2026-03-30
新增
IC 和 LT 模型新增激活节点记录功能:
添加
record_activated参数(默认为false),用于控制是否记录种子集激活的节点添加
set_record_activated(record)方法,可在运行时动态开启/关闭记录功能添加
run_single_simulation(seed=0)方法,用于单次独立传播模拟每次调用
run_single_simulation都会进行一次独立的传播模拟,从种子集出发,返回该次模拟中被激活的所有节点支持通过不同的
seed参数进行多次独立的模拟实验
API 更新
IndependentCascadeModel:
构造函数新增
record_activated参数新增
set_record_activated(record: bool)方法新增
run_single_simulation(seed: int = 0) -> Set[int]方法
LinearThresholdModel:
构造函数新增
record_activated参数新增
set_record_activated(record: bool)方法新增
run_single_simulation(seed: int = 0) -> Set[int]方法
测试
新增功能验证测试:
IC 模型单次模拟测试
LT 模型单次模拟测试
多次独立模拟测试(使用不同 seed)
set_record_activated动态切换测试编译和运行测试全部通过
技术细节
修改文件:
src/pynetim/cpp/include/diffusion_model.h- 添加核心功能实现src/pynetim/cpp/bindings/ic_bind.cpp- IC 模型 Python 绑定src/pynetim/cpp/bindings/lt_bind.cpp- LT 模型 Python 绑定src/pynetim/cpp/diffusion_model/independent_cascade_model.pyi- 类型提示src/pynetim/cpp/diffusion_model/linear_threshold_model.pyi- 类型提示
实现细节:
run_single_trial方法新增activated_nodes参数,用于记录激活节点当
record_activated为true时,遍历所有节点收集激活的节点使用独立的随机数生成器确保每次模拟的独立性
保持向后兼容性,
record_activated默认为false
使用示例
# IC 模型
from pynetim.cpp.diffusion_model import IndependentCascadeModel
ic_model = IndependentCascadeModel(graph, seeds, record_activated=True)
activated_nodes = ic_model.run_single_simulation(seed=42)
print(f"激活的节点: {activated_nodes}")
# LT 模型
from pynetim.cpp.diffusion_model import LinearThresholdModel
lt_model = LinearThresholdModel(graph, seeds, theta_l=0.0, theta_h=1.0, record_activated=True)
activated_nodes = lt_model.run_single_simulation(seed=42)
print(f"激活的节点: {activated_nodes}")
# 动态开启/关闭记录
ic_model.set_record_activated(True)
[0.4.2] - 2026-03-24
修复
C++ 扩展线程安全修复:
修复
IndependentCascadeModel和LinearThresholdModel在多线程模式下的线程安全问题通过参数传递替代直接访问成员变量
seeds,消除跨线程共享std::set的状态解决了与 PyTorch 等多线程库结合使用时的 segmentation fault 问题
经过严格测试:5000 次迭代、8 线程并发、10 图并发均稳定运行
单线程和多线程结果完全一致,确保正确性
测试
新增严格验证测试:
单线程/多线程一致性测试
长时间多线程压力测试 (2000 次迭代)
多线程并发访问测试 (8 线程)
多图并发处理测试 (10 个图)
极限压力测试 (5000 次迭代)
[0.4.0] - 2026-03-23
新增
C++ 图支持: 为所有工具函数添加了对 ``IMGraphCpp``(C++ 图)的完整支持
set_edge_weight()- 支持 C++ 图的边权重设置infection_threshold()- 支持 C++ 图的感染阈值计算graph_statistics()- 支持 C++ 图的统计信息计算graph_density()- 支持 C++ 图的密度计算connectivity_analysis()- 支持 C++ 图的连通性分析
优化
性能优化:
infection_threshold()- 避免重复计算度字典graph_statistics()- 使用Counter替代手动循环统计度分布connectivity_analysis()- 合并有向图和无向图的分支逻辑,消除重复代码graph_density()- 修复整数除法问题,确保返回浮点数
代码质量:
删除无用的
TYPE_CHECKING代码块统一函数签名,支持多种图类型
优化导入语句,添加缺失的
networkx模块
修复
Bug 修复:
修复
utils.py中缺失的import networkx as nx导入修复
graph_density()函数中的整数除法// 2导致的精度丢失修复
infection_threshold()函数中重复调用dict(graph.degree())的性能问题
文档
学术参考文献: 为所有主要模型和算法添加了学术参考文献
传播模型:
Linear Threshold Model - Granovetter (1978)
Independent Cascade Model - Kempe et al. (2003)
SIR Model - Kermack & McKendrick (1927)
SI Model - Kermack & McKendrick (1927)
影响力最大化算法:
Degree Discount Algorithm - Chen et al. (2009)
Reverse Influence Sampling (RIS) - Borgs et al. (2014)
IMM Algorithm - Tang et al. (2015)
工具函数:
infection_threshold()- Pastor-Satorras & Vespignani (2001, 2015)
新增文档:
UTILS_OPTIMIZATION_SUMMARY.md- utils.py 优化总结OPTIMIZATION_SUMMARY.md- C++ 模块优化总结REFERENCES_DOCUMENTATION.md- 参考文献添加文档CHANGELOG.md- 本更新日志
兼容性
向后兼容: 所有修改都保持了向后兼容性
NetworkX 图的使用方式完全不变
新增的 C++ 图支持是可选的
函数签名使用
Union类型,支持两种图类型
技术细节
图类型检测: 使用
hasattr(graph, 'num_nodes') and hasattr(graph, 'directed')检测 C++ 图类型注解: 使用
TYPE_CHECKING避免循环导入IMGraphCpp统一接口: NetworkX 图和 C++ 图使用相同的函数接口
[0.3.0] - 2025-XX-XX
新增
初始版本发布
实现了多种影响力最大化算法
实现了多种传播模型
提供了 Python 和 C++ 双实现
版本说明
PyNetIM 遵循 语义化版本控制 (Semantic Versioning):
主版本号 (MAJOR): 不兼容的 API 修改
次版本号 (MINOR): 向下兼容的功能性新增
修订号 (PATCH): 向下兼容的问题修正
版本类型
主版本更新: 重大架构变更、API 不兼容
次版本更新: 新增功能、性能优化
修订版本更新: Bug 修复、文档更新
更新分类
功能新增
新的算法实现
新的传播模型
新的图操作功能
性能优化
算法效率提升
内存使用优化
计算速度提升
Bug 修复
代码错误修复
边界情况处理
兼容性问题修复
文档改进
API 文档更新
示例代码更新
学术参考文献添加
内部改进
代码重构
测试覆盖提升
构建流程优化
贡献指南
如果您想为 PyNetIM 做出贡献,请:
Fork 本仓库
创建特性分支 (
git checkout -b feature/AmazingFeature)提交更改 (
git commit -m 'Add some AmazingFeature')推送到分支 (
git push origin feature/AmazingFeature)开启 Pull Request
许可证
本项目采用 MIT 许可证 - 详见 LICENSE 文件。
联系方式
作者: Zhang Kaijing
问题反馈: GitHub Issues
最后更新: 2026-04-04