47、稀疏矩阵的Cholesky分解及相关算法

稀疏矩阵的Cholesky分解及相关算法

在处理线性方程组时,稀疏矩阵的Cholesky分解是一个重要的问题。本文将详细介绍稀疏矩阵Cholesky分解的相关内容,包括超级节点算法、存储方案以及并行实现等方面。

超级节点Cholesky分解算法

超级节点Cholesky分解算法是一种用于稀疏矩阵Cholesky分解的有效方法。该算法通过将与超级节点相关的计算组合在一起,提高了计算效率。

算法原理

该算法使用 smod() 过程对列进行修改,根据列是否属于超级节点,有两种修改情况:
- 当列 j 属于超级节点 J 时,列 j 仅由 J 中位于其左侧的列修改。
- 当列 j 不属于超级节点 J 时,列 j J 的所有列修改。

以下是超级节点Cholesky分解算法的计算方案,也称为右视超级节点算法:

supernode_cholesky =
for each supernode J do from left to right {
    cdiv(first(J));
    for j = first(J) + 1, ..., last(J) {
        smod(j, J);
        cdiv(j);
    }
    for k ∈ Struct(L*(last(J)))
        smod(k, J);
}

该算法仍然从左到右计算 L 的列。与之前的算法不同的是,它将与超级节点相关的计算组合在一起。在超级节点级别,使用右视方案:对于超级节点 J 的第一列的计算,当与左侧所有列的修改完成后,只需要一次 cdiv() 操作。 J 的列以左视方式计算:在计算超级节点 J 左侧的所有超级节点后,由于右视超级节点方案, J 的列已经用这些超级节点进行了修改,因此列 j 的计算是先将其与 J 中位于其左侧的所有列进行修改,然后执行 cdiv() 操作。在计算完 J 的所有列后, J 右侧依赖于 J 列的所有列 k 都用 J 中的每一列进行修改,即通过 smod(k, J) 过程。

算法优势

超级节点算法的一个优点是增加了内存访问的局部性。因为超级节点 J 的每一列用于修改 J 右侧的几列,并且 J 的所有列用于修改 J 右侧的相同列。

稀疏矩阵的存储方案

由于稀疏矩阵中的大多数元素为零,因此使用特定的存储方案来避免存储零元素。这些压缩存储方案存储非零元素以及关于行和列索引的额外信息,以确定其在完整矩阵中的原始位置。

基本存储方案

稀疏下三角矩阵 L 以大小为$O(n + nz)$的压缩存储方案存储,其中 n L 中的行数(或列数), nz 是非零元素的数量。存储方案使用两个长度为 nz 的数组 Nonzero Row ,以及三个长度为 n 的数组 StartColumn StartRow Supernode
- Nonzero 数组按列主序包含三角矩阵$L = (l_{kj}) {k \geq j}$的所有非零元素的值,即非零元素在一个线性数组中从左到右按列排序。
- StartColumn 数组隐式包含非零元素的对应列索引信息。位置 j 存储 Nonzero 数组中列 j 的第一个非零元素的索引,即 Nonzero[StartColumn[j]] 包含$l
{jj}$。
- Row 数组包含 Nonzero 中对应元素的行索引。在不考虑超级节点的简单版本中, Row[r] 包含存储在 Nonzero[r] 中的非零元素的行索引,$r = 0, …, nz - 1$。

考虑超级节点的存储方案

当进一步利用同一超级节点中行的相似稀疏结构时,非零元素的行索引以 Row StartRow 数组的组合方式存储:
- StartRow[j] 存储 Row 中列 j 的第一个非零元素的行索引,即 Row[StartRow[j]] = j ,因为$l_{jj}$是第一个非零元素。
- 对于每一列,行索引仍然存储在 Row 的连续块中。与简单方案不同的是,同一超级节点中不同行的块不是不相交的,而是根据这些列的相似稀疏结构重叠。

通过以下方式可以快速访问集合 Struct(L*j)

Struct(L*j) =
{Row[StartRow[j] + i] | 0 ≤ i ≤ StartColumn[j + 1]
 - StartColumn[j - 1]}

Supernode 数组用于管理超级节点:如果列 j 是超级节点 J 的第一列,则 J 的列数存储在 Supernode[j] 中。

共享变量的并行实现

对于稀疏Cholesky分解的并行实现,考虑使用共享内存机器。稀疏Cholesky分解有多种并行源,包括单操作 cmod(j, k) cdiv(j) 内的细粒度并行性,以及左视、右视和超级节点算法中的列导向并行性。

消除树与并行性

稀疏矩阵 L 的稀疏结构可能导致额外的并行源,这在密集分解中是不可用的。当不同的列(以及对它们有影响的列)具有不相交的稀疏结构时,可以避免数据依赖。这种并行性可以通过消除树来描述,消除树使用 parent(j) 关系表达列之间数据依赖的特定情况。
- 对于每一列 j ,$0 \leq j < n$,定义 parent(j) 为:
plaintext parent(j) = min{i | i ∈ Struct(L*j)} if Struct(L*j) ≠ ∅
parent(j) 是列 j 的第一个非对角非零元素的行索引。如果 Struct(L*j) = ∅ ,则 parent(j) = j
- 对于$0 \leq i < n$,定义 children(i) 为:
plaintext children(i) = {j < i | parent(j) = i}
children(i) 包含所有在第 i 行有第一个非对角非零元素的列 j

有向图$G = (V, E)$,其中节点集$V = {0, …, n - 1}$,每个列对应一个节点,边集$E$,如果$i = parent(j)$且$i \neq j$,则$(i, j) \in E$。如果矩阵 A 是不可约的,则可以证明$G$是一棵树。

消除树$G$的一个重要性质是它指定了列必须评估的顺序: parent 的定义意味着如果$j = parent(i)$,则列 i 必须在列 j 之前评估。因此,列 j 的所有子节点必须在计算 j 之前完全评估。此外,列 j 不依赖于不在子树$G[j]$中的任何列。因此,如果$G[i]$和$G[j]$是不相交的子树,则列 i j 可以并行计算。特别是,消除树的所有叶子节点可以并行计算,并且计算不需要从列0开始。因此,稀疏结构决定了要利用的并行性。对于给定的矩阵,高度较小的消除树通常比高度较大的树表示更大的并行度。

并行左视算法

并行左视算法(III)的实现基于 n 个列任务 Tcol(0), ..., Tcol(n - 1) ,其中任务 Tcol(j) ,$0 \leq j < n$,包括对所有$k \in Struct(Lj*)$执行 cmod(j, k) 操作以及执行 cdiv(j) 操作。这些任务由于非零元素而相互依赖。并行实现使用任务池来管理任务的执行。任务池有一个中央任务池,用于存储列任务,每个处理器都可以访问。每个处理器负责执行一部分列任务。任务到处理器的分配是动态的,即当处理器空闲时,它从中央任务池获取一个任务。

动态实现的优点是工作负载分布均匀,尽管由于稀疏结构,任务的执行时间可能不同。处理器对中央任务池的并发访问必须无冲突,以确保任务唯一分配给处理器执行。这可以通过锁定机制实现,使得在特定时间只有一个处理器访问任务池。

有三种并行实现变体:
- 变体L1 :在所有$k \in Struct(Lj )$的列任务 Tcol(k) 完成之前,不将列任务 Tcol(j) 插入任务池。任务池可以初始化为消除树的叶子节点。并行度受树的独立节点数量限制,因为相互依赖的任务按顺序执行。因此,访问任务 Tcol(j) 的处理器可以执行该任务而无需等待其他任务完成。
-
变体L2 :允许在不需要立即完成执行的情况下开始执行 Tcol(j) 。任务池初始化为所有可用的列任务。列任务由处理器从左到右动态访问,即空闲处理器访问尚未分配给处理器的下一列。在所有$k \in Struct(Lj )$的任务 Tcol(k) 完成之前开始计算列任务 Tcol(j) 。在这种情况下, Tcol(j) 的并非所有 cmod(j, k) 操作都能立即执行,任务只能执行那些对应的任务已经执行的$k \in Struct(Lj*)$的 cmod(j, k) 操作。因此,任务在执行过程中可能需要等待其他任务完成。

以下是变体L2的代码实现:

parallel left cholesky =
c = 0;
while ((j = get_unique_index()) < n) {
    for (i = 0; i < |Struct(Lj*)|; i++) {
        while (Sj empty) wait();
        k = #pop(Sj);
        cmod(j, k);
    }
    cdiv(j);
    for (i ∈ Struct(L*j)) : push(j, Si);
}

为了控制单个列任务 Tcol(j) 的执行,为每一列 j 分配一个数据结构 Sj ,其中包含所有$k \in Struct(Lj )$的列,对于这些列, cmod(j, k) 操作已经可以执行。当处理器完成列任务 Tcol(k) 的执行(通过执行 cdiv(k) )时,它将 k 推送到每个$j \in Struct(L k)$的数据结构 Sj 上。由于不同的处理器可能同时尝试访问同一堆栈,因此必须使用锁定机制来避免访问冲突。执行 Tcol(j) 的处理器从 Sj 中弹出列索引 k 并执行相应的 cmod(j, k) 操作。如果 Sj 为空,处理器等待另一个处理器插入新的列索引。当从 Sj 中检索到$|Struct(Lj*)|$个列索引时,任务 Tcol(j) 可以执行最终的 cdiv(j) 操作。

  • 变体L3 :是L2的一种变体,考虑了消除树的结构。列不是严格从左到右分配给处理器,而是根据它们在消除树中的高度分配,即消除树中列 j 的子节点在其父节点 j 之前分配给处理器。这种变体试图按照完成其他列所需的顺序完成列任务,从而利用矩阵稀疏结构提供的额外并行性。
并行右视算法

并行右视算法(IV)的实现也基于任务池和列任务。列任务的定义与并行左视算法的任务不同:列任务 Tcol(j) ,$0 \leq j < n$,包括执行 cdiv(j) 和对所有$k \in Struct(L j)$执行 cmod(k, j) 操作,即列任务包括列 j 的最终计算以及对列 j 右侧所有依赖于 j 的列 k > j 的修改。任务池初始化为与消除树的叶子节点对应的所有列任务。一个非叶子任务 Tcol(j) 在对所有$k \in Struct(Lj )$执行 cmod(j, k) 操作并且可以执行最终的 cdiv(j) 操作时插入任务池。

以下是并行右视算法的代码实现:

parallel right cholesky =
c = 0;
initialize_task_pool(TP);
while ((j = get_unique_index()) < n) {
    while (!filled_pool(TP, j)) wait();
    get_column(j);
    cdiv(j);
    for (k ∈ Struct(L*j)) {
        lock(k); cmod(k, j); unlock(k);
        if (add_counter(ck, 1) + 1 == |Struct(Lk*)|) add_column(k);
    }
}

任务分配通过为每一列 j 维护一个计数器 cj 来实现。计数器初始化为0,并在每次执行 cmod(j, *) 操作后由相应的处理器使用无冲突的 add_counter() 过程递增。对于任务 Tcol(j) cmod(k, j) 操作的执行,必须锁定列 k ,以防止其他任务同时修改同一列。当计数器 cj 达到值$|Struct(Lj*)|$时,任务 Tcol(j) 插入任务池。

右视实现与左视变体L2的区别在于 cmod() 操作的执行顺序和执行处理器。在L2变体中, cmod(j, k) 操作由计算列 k 的处理器通过将其推送到堆栈 Sj 上发起,但该操作由计算列 j 的处理器执行。此执行不需要在操作发起后立即执行。在右视变体中, cmod(j, k) 操作不仅由计算列 k 的处理器发起,而且由该处理器执行。

综上所述,稀疏矩阵的Cholesky分解及相关算法在处理线性方程组时具有重要的应用价值。通过合理选择存储方案和并行实现方式,可以提高计算效率和性能。不同的算法变体适用于不同的场景,需要根据具体情况进行选择。

稀疏矩阵的Cholesky分解及相关算法

算法对比与总结

为了更清晰地了解各种算法和存储方案的特点,下面通过表格对前面介绍的内容进行总结和对比。

算法/方案 特点 优势 劣势 适用场景
超级节点Cholesky分解算法 将与超级节点相关的计算组合,右视和左视结合计算列 增加内存访问局部性 实现相对复杂 稀疏矩阵且对内存访问效率有要求的场景
基本存储方案(不考虑超级节点) 使用 Nonzero Row StartColumn 数组存储非零元素及索引信息 简单直观 未充分利用超级节点稀疏结构 稀疏结构简单,无明显超级节点特征的矩阵
考虑超级节点的存储方案 结合 Row StartRow 数组存储行索引,利用超级节点相似稀疏结构 存储更紧凑 理解和实现难度较大 具有明显超级节点结构的稀疏矩阵
并行左视算法 - 变体L1 任务按消除树独立节点顺序执行,任务池初始化为叶子节点 执行任务无需等待 并行度受独立节点数量限制 消除树独立节点较多的矩阵
并行左视算法 - 变体L2 任务可提前开始执行,动态从左到右分配任务 工作负载分布均匀 任务可能需等待其他任务完成 任务执行时间差异大的矩阵
并行左视算法 - 变体L3 考虑消除树高度分配任务,利用矩阵稀疏结构并行性 充分利用稀疏结构并行性 实现较复杂 具有明显稀疏结构且可通过消除树高度优化的矩阵
并行右视算法 列任务包含最终计算和右侧列修改,任务池初始化为叶子节点 操作执行顺序和处理器分配明确 需维护计数器和锁定列 对列修改操作顺序有要求的场景
流程图展示

下面通过mermaid格式的流程图来展示并行左视算法变体L2和并行右视算法的执行流程。

graph TD;
    A[开始] --> B[c = 0];
    B --> C{j = get_unique_index() < n};
    C -- 是 --> D{Si 非空};
    D -- 是 --> E[k = #pop(Si)];
    E --> F[cmod(j, k)];
    F --> G{是否完成所有 cmod(j, *)};
    G -- 是 --> H[cdiv(j)];
    H --> I{是否完成所有列任务};
    I -- 否 --> C;
    I -- 是 --> J[结束];
    D -- 否 --> K[等待];
    K --> D;
    C -- 否 --> J;

这个流程图展示了并行左视算法变体L2的执行流程,从初始化开始,不断获取列索引,根据堆栈状态执行 cmod 操作,完成后执行 cdiv 操作,直到所有列任务完成。

graph TD;
    A[开始] --> B[c = 0];
    B --> C[initialize_task_pool(TP)];
    C --> D{j = get_unique_index() < n};
    D -- 是 --> E{filled_pool(TP, j)};
    E -- 是 --> F[get_column(j)];
    F --> G[cdiv(j)];
    G --> H{k ∈ Struct(L*j)};
    H --> I[lock(k)];
    I --> J[cmod(k, j)];
    J --> K[unlock(k)];
    K --> L{add_counter(ck, 1) + 1 == |Struct(Lk*)|};
    L -- 是 --> M[add_column(k)];
    L -- 否 --> H;
    M --> N{是否完成所有列任务};
    N -- 否 --> D;
    N -- 是 --> O[结束];
    E -- 否 --> P[等待];
    P --> E;
    D -- 否 --> O;

这个流程图展示了并行右视算法的执行流程,包括任务池初始化、获取列任务、执行 cdiv cmod 操作,以及根据计数器状态插入任务等步骤,直到所有列任务完成。

实际应用中的考虑因素

在实际应用中,选择合适的算法和存储方案需要考虑多个因素。

  • 矩阵的稀疏结构 :如果矩阵具有明显的超级节点结构,那么超级节点Cholesky分解算法和考虑超级节点的存储方案可能更合适。例如,在一些物理模拟场景中,矩阵的稀疏结构可能与物理系统的拓扑结构相关,存在明显的超级节点特征。
  • 并行性需求 :如果需要充分利用多核处理器的并行计算能力,那么并行算法是必要的。根据矩阵的消除树结构和任务执行时间的差异,可以选择不同的并行算法变体。例如,对于消除树独立节点较多的矩阵,并行左视算法变体L1可能更合适;而对于任务执行时间差异较大的矩阵,变体L2可能更能平衡工作负载。
  • 内存使用 :不同的存储方案对内存的使用效率不同。考虑超级节点的存储方案可以更紧凑地存储矩阵,但实现和理解难度较大。在内存资源有限的情况下,需要权衡存储方案的复杂度和内存使用效率。
总结与展望

稀疏矩阵的Cholesky分解及相关算法在处理大规模线性方程组时具有重要的应用价值。通过合理选择存储方案和并行实现方式,可以显著提高计算效率和性能。不同的算法变体适用于不同的场景,需要根据矩阵的具体特征和应用需求进行选择。

随着计算机硬件的不断发展,多核处理器和分布式计算环境的普及,未来对于稀疏矩阵算法的研究将更加注重并行性和可扩展性。同时,如何更好地利用矩阵的稀疏结构和超级节点特征,进一步优化存储方案和算法实现,也是一个值得深入研究的方向。此外,在实际应用中,如何将这些算法与具体的领域知识相结合,提高算法的实用性和性能,也是未来的研究重点之一。

总之,稀疏矩阵的Cholesky分解及相关算法是一个充满挑战和机遇的研究领域,对于推动科学计算和工程应用的发展具有重要意义。

六自由度机械臂ANN人工神经网络设计:正向逆向运动学求解、正向动力学控制、拉格朗日-欧拉法推导逆向动力学方程(Matlab代码实现)内容概要:本文档围绕六自由度机械臂的ANN人工神经网络设计展开,详细介绍了正向与逆向运动学求解、正向动力学控制以及基于拉格朗日-欧拉法推导逆向动力学方程的理论与Matlab代码实现过程。文档还涵盖了PINN物理信息神经网络在微分方程求解、主动噪声控制、天线分析、电动汽车调度、储能优化等多个工程与科研领域的应用案例,并提供了丰富的Matlab/Simulink仿真资源和技术支持方向,体现了其在多学科交叉仿真与优化中的综合性价值。; 适合人群:具备一定Matlab编程基础,从事机器人控制、自动化、智能制造、电力系统或相关工程领域研究的科研人员、研究生及工程师。; 使用场景及目标:①掌握六自由度机械臂的运动学与动力学建模方法;②学习人工神经网络在复杂非线性系统控制中的应用;③借助Matlab实现动力学方程推导与仿真验证;④拓展至路径规划、优化调度、信号处理等相关课题的研究与复现。; 阅读建议:建议按目录顺序系统学习,重点关注机械臂建模与神经网络控制部分的代码实现,结合提供的网盘资源进行实践操作,并参考文中列举的优化算法与仿真方法拓展自身研究思路。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值