Coding Poet, Coding Science

Lapack矩阵运算库

所打印的《the lapacke interface to lapack》原来来自于大二时候学习使用LAPACK软件包,但是FORTRAN语言本身又比较复杂,因而寻找一个外在的接口函数库而找到的。不过事实上它没有发挥过什么作用,仅仅被看过一遍就被遗忘在角落里面了。现在为了把无用的东西都扔掉,整理一本笔记。

lapack原来是用FORTRAN语言写成的,后来C语言得到了广泛的应用,所以创建了面向C语言的接口,尽管可能完全重写更有可能得到更高的效率。于是到现在我们就得为了使用lapack而学习如何使用接口。

面向C语言的接口还分成两个层次。对于开发者来说,两个层次的最重要的区别是内存管理是否需要手工进行,对于调用者来说,则转化成命名的前缀与后缀的问题。其实较低级的程序员一直都很难避免陷于各种移植,各种前缀当中。

lapack的高级接口由内部的库负责内存的管理。但是中级的接口则是需要用户向一系列的库函数传递数组。两种接口均各自提供行主与列主的版本。主要数据类型与主要函数的声明都放在lapack.h文件里面。

高级接口的命名方式是在FORTRAN程序集名称上加上前缀 LAPACKE_ ,而中级接口则是加上相关的前缀的同时加上 _work 。注意在C语言接口中函数都使用小写的名子。

数据类型

(我们知道浮点数是有相应的IEEE规范的)。复数类型在C语言里面分别对应于 lapack_complex_float , lapack_complex_double。在实现中要,复数单精度浮点类型实际可能使用的是C99复数类型,可能是C语言的结构体,也可能是CC的STL模板。具体定义就要见lapacke.h了。

在lapack当中,向量是自动决定是行向量还是列向量的。但是矩阵就不一样了。矩阵的行主或者列主在传递参数的时候要通过LAPACK_ROW_MAJORLAPACK_COL_MAJOR来指明。在函数调用规范中,如果一个矩阵在经过此函数处理的时候是行主或列主的,那么其它的矩阵也要保持相同的定义。

注意使用行主的时候会比列主消耗更多的内存,因而花费更多的时间。在计算过程中行主的矩阵会转成列主的形式参加计算。(这种转换发生在向LAPACK传递数据之前。)

每个LAPACK的返回值被放在 lapack_int类型的数据中,其名称为INFO。和其它的库函数类似,我们经常得检查是否被调用的函数发生了异常,以及发生了何种异常。

错误号在LAPACKE与LAPACKE当中数值序号是相同的。

在LAPACKE高级库中有一个NaN特性,允许在调用LAPACK之前进行参数的检查工作。这种检查之后,相应的结果会在INFO变量中表现出来。通过指定LAPACK_DISABLE_NAN_CHECK 我们可以禁止进行这种检查。

FORTRAN的整数INTEGER类型一般情况下被转成 lapack_int 类型。后者又进一步变成相应的标准整数类型。但是这带来一个C语言的规定性问题。如果在一台机器上C语言整数是4bit,那么它就只能调用4bit的整数。

FORTRAN中的LOGIC类型会被同样地转成 lapack_int 类型。在LAPACKE库当中,为一系列的错误定义了枚举值。可以通过相应的名子在源代码中检查是否出现了相应的错误。

FORTRAN中函数的命名规则

使用单精度实数作为输入参数的函数以s为标识,又精度以d为标识,单精度复数以c为标识,而双精度复数以z为标识。

不过还是有一些使用混合类型的运算方式。

LAPACKE调用示例

函数调用的方法是首先使用相应的类型声明整型,然后声明使用的相关的矩阵。注意在LAPACKE当中需要使用一维数组按照行主或者列主的方式存储相应的矩阵,而不是使用指向指针的指针。

计算列主矩阵的每行元素有lda个的元素的QR分解可以参考下面的例子:

lapack_int m, n, lda, info;
double *a, *tau;
info = LAPACKE_deqrf(LAPACK_COL_MAJOR, m, n, a, lda, tau);

其中各个元素的含义是矩阵A的行数是m,列数是n.行列式中的每一个元素是lda个(真不知道设计leading dimission有什么作用)。

当使用中层接口的时候,我们要使用存在于LAPACKE内部的内存分配与释放函数了。它们分别是 LAPACKE_mallocLAPACKE_free。不过在这种情况下我们也不用自己 调用内存分配函数,这些分配与释放会由相应带work后缀的元素自己使用。

其实我们应该记住LAPACKE中不使用C语言提供的多维数组就行了。