如何从 Chapel 中的文件中读取矩阵

How to read a matrix from a file in Chapel

这次我有一个名为 "matrix.csv" 的矩阵——在一个文件中——我想读入它。我可以用两种方式来读它,密集的和稀疏的。

密集

matrix.csv

3.0, 0.8, 1.1, 0.0, 2.0
0.8, 3.0, 1.3, 1.0, 0.0
1.1, 1.3, 4.0, 0.5, 1.7
0.0, 1.0, 0.5, 3.0, 1.5
2.0, 0.0, 1.7, 1.5, 3.0

稀疏

matrix.csv
1,1,3.0
1,2,0,8
1,3,1.1
// 1,4 is missing
1,5,2.0
...
5,5,3.0

假设文件很大。在这两种情况下,我都想将它们读入具有适当尺寸的矩阵中。在密集的情况下,我可能不需要提供元数据。第二,我想我应该提供矩阵的"frame",比如

matrix.csv
nrows:5
ncols:5

但是我不知道标准模式。

==更新==

有点难找,但是 mmreadsp 可以让你的一天从 "Crashing the server" 变成 "done in 11 seconds"。感谢 Brad Cray(化名)指出!

向教授致敬。 Rudolf Zitny 和教授。彼得·沃彭卡

(如果你碰巧记得 PC 工具实用程序,矩阵工具,由 Zitny 教授首创和创作,对于大规模 F77 FEM 矩阵的智能抽象表示同样不可或缺,使用 COMMON-block 和类似的技巧数值处理项目中的大型稀疏矩阵高效存储和操作...)


观察:

我完全不同意最后一条关于需要 "frame" 以构建稀疏矩阵的评论。

矩阵永远只是某种形式主义的解释

虽然稀疏矩阵在矩阵上共享相同的视图,作为解释,每个此类模块的实现始终严格基于一些具体的代表.

不同类型的稀疏性总是使用不同的单元格布局策略来处理(诀窍是对单元格元素使用最小需要的[SPACE],同时还要有一些可接受的处理[TIME]开销,当尝试对此类矩阵执行 classical matrix/vector 操作时(通常在用户不知情的情况下或 "manually" 打扰用于存储单元格值的底层稀疏矩阵表示,以及如何将其最佳解码/翻译成目标稀疏矩阵的表示形式)。

直观地说,Matrix Tools 会向您显示每个 表示 在它们最好的内存布局中尽可能紧凑(非常像它在 PC Tools 中压缩你的硬盘,放置扇区数据以避免浪费任何不必要的非连续硬盘容量)和非常(逐类型特定)表示-然后,感知处理程序将为任何外部观察者提供假设矩阵所需的完整幻觉解释(在计算阶段)。

所以让我们首先意识到,不知道关于平台特定规则的所有细节,用于稀疏矩阵表示,无论是在源端(python-?, JSON-meta-payload-?, 等)和 目标端(LinearAlgebra ver-1.16尚未确认为 public ( W.I.P. ),没有太多可开始实施的。

一个(尚未知)稀疏矩阵表示的实际实现(可能是file://, DMA 访问或 CSP 通道或非 InRAM 存储或 InRAM 内存映射的任何其他方式)不会改变一个位的交叉表示 xlator 的解决方案。

作为一名数学家,您可能会喜欢 表示的概念 不是康托集驱动的( 运行 成(几乎)无限、密集的枚举)对象,而是使用 Vopenka 的另类集合论(在 Vopenka 的 "Meditations About The Bases of Science" 中深入介绍了历史和数学背景,非常可爱),它通过不断变化的定义范围(不仅由观察者观点的实际敏锐度引起,而且在这种原则的更广泛和普遍意义上引起),留下 pi-class 和 sigma-class 半集准备好连续处理新兴新的细节,因为它们进入我们关于观察到的(和数学化的)现象的观点的公认部分(一旦出现 "in front" 的定义范围)。

稀疏矩阵(作为表示)帮助我们构建我们需要的解释,以便使用目前获得的数据-进一步处理中的细胞 "as a matrix".

这就是说,工作流总是需要先验知道:

a)稀疏矩阵源系统的表示中使用的约束和规则
b) 中介通道施加的额外约束(表现力、格式、self-healing/error-prone),无论它是文件、CSP 通道还是 ZeroMQ / nanomsg 智能套接字信令/消息平面分布式代理基础设施
c) 在目标系统的 表示中施加的约束和规则 ,设置用于定义/加载/存储/进一步处理和计算稀疏的规则-选择的矩阵类型必须满足/遵循目标计算生态系统

不知道 a) 会在为成功和高效的交叉表示管道准备策略时引入不必要的大量开销,即翻译常见的 解释 来自源端 表示 用于输入 b)。忽略 c) 总是会导致惩罚——在 b) 介导的重建期间在目标生态系统中支付额外的开销对目标表示.

的传达解释

前言

由于 Chapel 矩阵表示为数组,因此本题等同于:

"How to read an array from a file in Chapel".

理想情况下,csv 模块或专门的 IO 格式化程序(类似于 JSON formatter)会更优雅地处理 csv I/O,但这个答案反映了数组I/O Chapel 1.16 预发布版可用的选项。

密集数组I/O

密集数组是最简单的情况,因为 DefaultRectangular 数组(Chapel 数组的默认 type)带有 .readWriteThis(f) 方法。此方法允许使用内置的 write()read() 方法读写数组,如下所示:

var A: [1..5, 1..5] real;

// Give this array some values
[(i,j) in A.domain] A[i,j] = i + 10*j;

var writer = open('dense.txt', iomode.cw).writer();

writer.write(A);

writer.close();

var B: [1..5, 1..5] real;
var reader = open('dense.txt', iomode.r).reader();
reader.read(B);
reader.close();

assert(A == B);

dense.txt 看起来像这样:

11.0 21.0 31.0 41.0 51.0
12.0 22.0 32.0 42.0 52.0
13.0 23.0 33.0 43.0 53.0
14.0 24.0 34.0 44.0 54.0
15.0 25.0 35.0 45.0 55.0

但是,这假定您事先知道数组的形状。我们可以通过在文件顶部写入数组形状来移除这个约束,如下所示:

var A: [1..5, 1..5] real;

[(i,j) in A.domain] A[i,j] = i + 10*j;

var writer = open('dense.txt', iomode.cw).writer();

writer.writeln(A.shape);
writer.write(A);

writer.close();

var reader = open('dense.txt', iomode.r).reader();
var shape: 2*int;
reader.read(shape);
var B: [1..shape[1], 1..shape[2]] real;
reader.read(B);
reader.close();

assert(A == B);

现在,dense.txt 看起来像这样:

(5, 5)
11.0 21.0 31.0 41.0 51.0
12.0 22.0 32.0 42.0 52.0
13.0 23.0 33.0 43.0 53.0
14.0 24.0 34.0 44.0 54.0
15.0 25.0 35.0 45.0 55.0

稀疏数组I/O

稀疏数组需要多做一些工作,因为 DefaultSparse 数组(稀疏 Chapel 数组的默认 type)仅提供 .writeThis(f) 方法而不提供 .readThis(f) Chapel 1.16 预发布时的方法。这意味着我们内置支持写入稀疏数组,但不支持读取它们。

由于您特别要求 csv 格式,我们将在 csv 中使用稀疏数组:

// Create parent domain, sparse subdomain, and sparse array
const D = {1..10, 1..10};
var spD: sparse subdomain(D);
var A: [spD] real;

// Add some non-zeros:
spD += [(1,1), (1,5), (2,7), (5, 4), (6, 6), (9,3), (10,10)];

// Set non-zeros to 1.0 (to make things interesting?)
A = 1.0;

var writer = open('sparse.csv', iomode.cw).writer();

// Write shape
writer.writef('%n,%n\n', A.shape[1], A.shape[2]);

// Iterate over non-zero indices, writing: i,j,value
for (i,j) in spD {
  writer.writef('%n,%n,%n\n', i, j, A[i,j]);
}

writer.close();

var reader = open('sparse.csv', iomode.r).reader();

// Read shape
var shape: 2*int;
reader.readf('%n,%n', shape[1], shape[2]);

// Create parent domain, sparse subdomain, and sparse array
const Bdom = {1..shape[1], 1..shape[2]};
var spBdom: sparse subdomain(Bdom);
var B: [spBdom] real;

// This is an optimization that bulk-adds the indices. We could instead add
// the indices directly to spBdom and the value to B[i,j] each iteration
var indices: [1..0] 2*int,
    values: [1..0] real;

// Variables to be read into
var i, j: int,
    val: real;
while reader.readf('%n,%n,%n', i, j, val) {
  indices.push_back((i,j));
  values.push_back(val);
}

// bulk add the indices to spBdom and add values to B element-wise
spBdom += indices;
for (ij, v) in zip(indices, values) {
  B[ij] = v;
}

reader.close();

// Sparse arrays can't be zippered with anything other than their domains and
// sibling arrays, so we need to do an element-wise assertion:
assert(A.domain == B.domain);
for (i,j) in A.domain {
  assert(A[i,j] == B[i,j]);
}

sparse.csv 看起来像这样:

10,10
1,1,1
1,5,1
2,7,1
5,4,1
6,6,1
9,3,1
10,10,1

矩阵市场模块

最后,我要提到有一个 MatrixMarket 包模块支持密集和稀疏数组 I/O 使用 matrix market format。这目前没有在 public 文档中显示,因为一旦包管理器足够可靠,它就会作为一个独立的包被移出,但是你可以在你的教堂程序中使用它 use MatrixMarket;,目前。

这是 source code,其中包括作为注释的界面文档。

这里是 tests,如果您更喜欢从示例中学习,而不是文档和源代码。