矩阵级(row-major Double()())批量 SIMD 内核门面。
SimdMatrix
00 Remarks
SimdEngine / SimdReduce / SimdMath / SimdParallel 都是面向一维连续数组的内核,而 Microsoft.VisualBasic.Math 中的矩阵对象内部数据是 “行数组的数组”(每行是一段连续内存)。这里把矩阵操作按行适配到 一维内核上,避免在每个调用点重复书写 For Each row + 内核分派的样板代码。
为什么按行而不是按列:行是连续内存,可以直接交给 New Vector(Of T)(array, offset) 批量装载;列方向是 strided access, 没有可用的向量化原语。需要列数据时先通过 SimdMatrix.ExtractColumn() 把它变成连续数组。
形状契约:与 SimdEngine 的逐元素内核不同,这里会校验 两个矩阵的行数与每行长度都一致,不一致时抛出 ArgumentException; 因为逐元素内核假设长度一致,缺少这层校验只会导致难以定位的越界访问。
回退:所有内核最终都会经过 SimdEngine.CanVectorize() 检查 SIMDEnvironment.IsEnabled,因此 SIMDConfiguration.disable 仍然是全局有效的逃生开关。
01 Syntax
02 Methods
| Name | Overloads | Summary |
|---|---|---|
| RowCount | 1 | 矩阵行数(Nothing 视作 0 行)。 |
| ColumnCount | 1 | 矩阵列数(以第一行的长度为基准)。 |
| CheckAgree | 1 | 校验两个矩阵的行数与每行长度都一致。 |
| NewMatrix | 1 | 分配 [rows, cols] 的矩形容器(每行独立分配,内容未初始化)。 |
| Add | 1 | 逐元素相加:out(i)(j) = a(i)(j) + b(i)(j) |
| Subtract | 1 | 逐元素相减:out(i)(j) = a(i)(j) - b(i)(j) |
| Multiply | 1 | 逐元素相乘:out(i)(j) = a(i)(j) * b(i)(j) |
| Divide | 1 | 逐元素标准相除:out(i)(j) = a(i)(j) / b(i)(j)(0/0 = NaN)。 |
| DivideZeroSafe | 1 | 逐元素相除,且分子为零时结果置零(避免 0/0 产生 Double)。 |
| AddInPlace | 1 | 就地逐元素相加。 |
| SubtractInPlace | 1 | 就地逐元素相减。 |
| MultiplyInPlace | 1 | 就地逐元素相乘。 |
| DivideInPlace | 1 | 就地逐元素相除。 |
| DivideZeroSafeInPlace | 1 | 就地逐元素相除(分子为零则结果置零)。 |
| AddScalar | 1 | 矩阵加标量:out(i)(j) = a(i)(j) + scalar |
| SubtractScalar | 1 | 矩阵减标量:out(i)(j) = a(i)(j) - scalar |
| ScalarSubtract | 1 | 标量减矩阵:out(i)(j) = scalar - a(i)(j) |
| MultiplyScalar | 1 | 数乘:out(i)(j) = scalar * a(i)(j) |
| DivideScalar | 1 | 矩阵除以标量:out(i)(j) = a(i)(j) / scalar |
| ScalarDivide | 1 | 标量除以矩阵:out(i)(j) = scalar / a(i)(j) |
| MultiplyScalarInPlace | 1 | 就地数乘:a(i)(j) = scalar * a(i)(j) |
| Negate | 1 | 逐元素取负。 |
| Abs | 1 | 逐元素绝对值。 |
| Sqrt | 1 | 逐元素平方根。 |
| Square | 1 | 逐元素平方。 |
| PowScalar | 1 | 逐元素固定次幂:out(i)(j) = a(i)(j) ^ exponent |
| Pow | 1 | 逐元素幂运算:out(i)(j) = a(i)(j) ^ b(i)(j) |
| Log | 1 | 逐元素以 base 为底的对数。 |
| Exp | 1 | 逐元素自然指数(无硬件指令,保持标量内核)。 |
| Transpose | 1 | 矩阵转置。 |
| ExtractColumn | 1 | 抽取指定列为一个连续的一维数组。 |
| MultiplyRows | 1 | 按行缩放:out(i)(j) = factors(i) * a(i)(j) |
| Norm1 | 1 | 1-范数:所有列绝对值之和中的最大值。 |
| NormInf | 1 | 无穷范数:所有行绝对值之和中的最大值。 |
| NormF | 1 | Frobenius 范数:SQRT(SUM(a(i)(j) ^ 2))。 |
| Trace | 1 | 矩阵的迹:主对角线元素之和。 |
| MaxIndex | 1 | 全矩阵最大值及其位置。 |
| MinIndex | 1 | 全矩阵最小值及其位置。 |
| Dot | 1 | 矩阵乘积 A * B:右矩阵预转置 + 行方向并行 + 行内 FMA 点积。 |
| MatrixVector | 1 | 矩阵向量乘积:out(i) = SUM(a(i)(j) * x(j)),使用 FMA 点积。 |
| Rank1Update | 1 | 秩一更新:a(i)(j) += alpha * x(i) * y(j),使用 FMA/AXPY 就地更新。 |
| AxpyInPlace | 1 | 就地 AXPY:y(i) += alpha * x(i),使用 FMA。 |
03 Members
Double()())矩阵行数(Nothing 视作 0 行)。
Double()())矩阵列数(以第一行的长度为基准)。
Double()(), Double()(), String, String)校验两个矩阵的行数与每行长度都一致。
Int32, Int32)分配 [rows, cols] 的矩形容器(每行独立分配,内容未初始化)。
Double()(), Double()())逐元素相加:out(i)(j) = a(i)(j) + b(i)(j)
Double()(), Double()())逐元素相减:out(i)(j) = a(i)(j) - b(i)(j)
Double()(), Double()())逐元素相乘:out(i)(j) = a(i)(j) * b(i)(j)
Double()(), Double()())逐元素标准相除:out(i)(j) = a(i)(j) / b(i)(j)(0/0 = NaN)。
Double()(), Double()())逐元素相除,且分子为零时结果置零(避免 0/0 产生 Double)。
Double()(), Double()())就地逐元素相加。
Double()(), Double()())就地逐元素相减。
Double()(), Double()())就地逐元素相乘。
Double()(), Double()())就地逐元素相除。
Double()(), Double()())就地逐元素相除(分子为零则结果置零)。
Double()(), Double)矩阵加标量:out(i)(j) = a(i)(j) + scalar
Double()(), Double)矩阵减标量:out(i)(j) = a(i)(j) - scalar
Double, Double()())标量减矩阵:out(i)(j) = scalar - a(i)(j)
Double()(), Double)数乘:out(i)(j) = scalar * a(i)(j)
Double()(), Double)矩阵除以标量:out(i)(j) = a(i)(j) / scalar
Double, Double()())标量除以矩阵:out(i)(j) = scalar / a(i)(j)
Double()(), Double)就地数乘:a(i)(j) = scalar * a(i)(j)
Double()())逐元素取负。
Double()())逐元素绝对值。
Double()())逐元素平方根。
Double()())逐元素平方。
Double()(), Double)逐元素固定次幂:out(i)(j) = a(i)(j) ^ exponent
指数为 2 / 3 / 4 / 0.5 时会走到向量化的快速路径,其余指数退回标量 ^。
Double()(), Double()())逐元素幂运算:out(i)(j) = a(i)(j) ^ b(i)(j)
Double()(), Double)逐元素以 base 为底的对数。
Double()())逐元素自然指数(无硬件指令,保持标量内核)。
Double()())矩阵转置。
使用 32x32 分块,把跨行的离散访问收拢成块内的顺序访问以降低 cache miss。 转置本身是纯数据搬运,没有可用的算术指令,因此不使用逐元素内核。
Double()(), Int32)抽取指定列为一个连续的一维数组。
跨行读取是 strided access,没有高效的向量化 gather 原语,因此保持顺序拷贝; 它的价值在于把列数据变成连续内存,让后续的列内积/列范数可以走向量化路径。
Double()(), Double())按行缩放:out(i)(j) = factors(i) * a(i)(j)
| Name | Type | Description |
|---|---|---|
factors | Double()() | 长度必须等于矩阵的行数 |
Double()())1-范数:所有列绝对值之和中的最大值。
逐行累加绝对值,列方向仍然按 [0, rows) 的顺序累加, 因此结果与原实现逐位一致(不存在归约重排); 累加过程使用就地内核,不需要为每一行生成绝对值临时数组。
Double()())无穷范数:所有行绝对值之和中的最大值。
Double()())Frobenius 范数:SQRT(SUM(a(i)(j) ^ 2))。
与原先 Hypot 逐步缩放实现一样具备抗上溢能力:先用 SimdReduce.MaxAbs() 探测矩阵的最大绝对值, 只有当它大到平方和可能上溢时(>= 1E+150)才切换到缩放路径。
常规量级的数据直接累加平方和,因此热路径上没有任何逐行临时数组; 这也让常见的 NormF 调用不再是「每行两次分配」的形态。
Double()())矩阵的迹:主对角线元素之和。
Double()(), Int32, Int32)全矩阵最大值及其位置。
只在严格大于当前最大值时更新位置,因此多个相同最大值时返回第一个出现的位置, 与原逐行逐列扫描的语义一致。空矩阵返回 Double 且位置保持 (0, 0)。
Double()(), Int32, Int32)全矩阵最小值及其位置。
Double()(), Double()())矩阵乘积 A * B:右矩阵预转置 + 行方向并行 + 行内 FMA 点积。
| Name | Type | Description |
|---|---|---|
a | Double()() | 左矩阵,形状 |
b | Double()() | 右矩阵,形状 |
Double()(), Double())矩阵向量乘积:out(i) = SUM(a(i)(j) * x(j)),使用 FMA 点积。
Double()(), Double(), Double(), Double)秩一更新:a(i)(j) += alpha * x(i) * y(j),使用 FMA/AXPY 就地更新。
| Name | Type | Description |
|---|---|---|
a | Double()() | 目标矩阵,形状 |
x | Double() | 长度 |
y | Double() | 长度 |
alpha | Double | 缩放因子;需要做减法时传入负值即可 |
Double, Double(), Double())就地 AXPY:y(i) += alpha * x(i),使用 FMA。