LAPACK 是 Linear Algebra PACKage 的縮寫,它是一個以 Fortran 語言寫成的函式庫。 本篇的目標,是要以矩陣相乘等常見的函式為例,來介紹 LAPACK 的使用。 由於自己下載原始碼,並編譯出函式庫的步驟比較繁瑣, 所以這裡提供了事先編譯好的 lapack.rar 讓各位下載, 請解壓縮後把它們放在同一個資料夾,例如"C:\lapack"。 (如果對自行編譯有需要的話,可以檢視本網頁的原始碼,裡面用註解的方式記錄了一些比較重要的步驟。)
接著,我們要讓 Code::Blocks 知道函式庫放在哪裡。請先選擇 Settings → Compiler,應該會出現這個畫面:
再來,請找到 linker settings 這個分頁,把三個 *.a 的函示庫加入路徑中
下一步,請找到 Search directories 這個分頁, 加入 C:\lapack 或你放置的其他路徑,才能讓 Code::Blocks 找到相對應的 *.h 檔案。現在,你可以先試著編譯與執行這個程式:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <f2c.h>
#include <clapack.h>
void
matrixDisplay
(
double
mat[]
,
int
m
,
int
n
)
{
int
i
,
j;
for
(
i=0;i<m;i++
)
{
for
(
j=0;j<n;j++
)
{
printf
(
"%.0f "
,
mat[i*n+j]
)
;
}
printf
(
"\n"
)
;
}
}
int
main
(
)
{
int
n;
double
*A
,
*B
,
*C;
double
alpha=1.0
,
beta=0.0;
char
trans=
't'
;
int
i
,
j;
clock_t
tic
,
toc;
/* space allocation */
n = 2;
A =
(
double
*
)
malloc
(
n*n*
sizeof
(
double
)
)
;
B =
(
double
*
)
malloc
(
n*n*
sizeof
(
double
)
)
;
C =
(
double
*
)
malloc
(
n*n*
sizeof
(
double
)
)
;
/* value generation/initialization */
srand
(
time
(
NULL
)
)
;
j = n*n;
for
(
i=0; i<j; i++
)
{
A[i] =
rand
(
)
%5+1;
B[i] =
rand
(
)
%5+1;
C[i] = 0.0;
}
/* computation */
tic =
clock
(
)
;
dgemm_
(
&trans
,
&trans
,
&n
,
&n
,
&n
,
&alpha
,
A
,
&n
,
B
,
&n
,
&beta
,
C
,
&n
)
;
toc =
clock
(
)
;
printf
(
"%.4f\n"
,
(
double
)
(
toc-tic
)
/CLOCKS_PER_SEC
)
;
/* display */
printf
(
"==================\n"
)
;
matrixDisplay
(
A
,
n
,
n
)
;
printf
(
"==================\n"
)
;
matrixDisplay
(
B
,
n
,
n
)
;
printf
(
"==================\n"
)
;
matrixDisplay
(
C
,
n
,
n
)
;
printf
(
"==================\n"
)
;
free
(
A
)
;
free
(
B
)
;
free
(
C
)
;
return
0;
}
這個程式會亂數產生兩個 2-by-2 的矩陣,然後利用 LAPACK 計算矩陣乘積。 首先,要使用 LAPACK 的功能,必須含括 f2c.h 和 clapack.h。 time.h 則是計時用,在此範例中,由於矩陣很小,所以時間不明顯。 再來,對於矩陣的部分,我們必須使用一維矩陣來存放資料,然後自行對應至二維。
然後是最重要的,呼叫 LAPACK 的部分,我們使用的是「dgemm_」這個函式。 它會幫你計算這個式子:
C = α*op(A)*op(B) + β*C
其中,op() 代表矩陣要轉置或者不要轉置,由輸入參數決定。 命名的規則稍後來說明,我們先介紹參數如下, 完整的說明可以參考這裡:
- transA: 控制 A 矩陣是否轉置;若值為'N'或'n'時代表不轉置,'T'或't'時代表轉置。
- transB: 控制 B 矩陣是否轉置;若值為'N'或'n'時代表不轉置,'T'或't'時代表轉置。
- m: op(A) 矩陣的 rows 的個數。
- n: op(B) 矩陣的 columns 的個數。
- k: op(A) 矩陣的 columns 的個數,即 op(B) 矩陣的 rows 的個數。
- α: 如前述式子。
- A: A 矩陣。
- LDA: A 未轉置時為 m,轉置時為 k。
- B: B 矩陣。
- LDB: B 未轉置時為 k,轉置時為 n。
- β: 如前述式子。
- C: C 矩陣。
- LDC: 與 n 同值。
所以,如果要進行兩個方陣 A 和 B 的相乘,則矩陣大小皆為 n,α 為 1,β 為 0。 在 trans 的部分,由於在 C 語言裡面,把二維放成一維時,習慣一個 row 接一個 row 放(稱為 row-major); 而 LAPACK 使用的 Fortran 語言,習慣一個 column 接一個 column 放(稱為 column-major), 所以 trans 在此處皆使用 't'。 當然,輸出的 C 矩陣是 column-major,而此處在印出前並沒有特別轉換,所以你會覺得看到的矩陣方向不太對。 另外,此函式庫需要傳入記憶體位置,所以若是非指標型態的變數,記得加上「&」。接下來我們介紹命名規則。由於早期的 Fortran 在函式名稱的字數上有些限制, 因此有規則如下(少數特例本篇不介紹):
- 第一個字母代表變數類型,LAPACK 有四種變數類型:
- single (s): 單精度浮點數,即 C 語言的 float。
- double (d): 倍精度浮點數,即 C 語言的 double。
- complex (c): 單精度複數,即 C 語言的 struct { float r, i; }。
- doublecomplex (z): 單精度浮點數,即 C 語言的 struct { double r, i; }。
- 再來的兩個字母代表矩陣的樣子,部分舉例如下:
- GE: general,一般的矩陣。
- DI: diagonal,只有對角線的矩陣。
- SY: symmetric,對稱矩陣
- 最後的幾個字代表要進行的運算,例如:
- TRF: 三角化,即 LU 分解。
- TRS: 解 AX=B,其中 A 是 xyyTRF 計算出的三角矩陣。
- MM: 矩陣乘積。
因為 LAPACK 提供的函式很多,本篇就不一一介紹。 以下示範 double 類型的 LU 分解的範例,需要使用的函式是「dgetrf_」,參數說明如下:
- m: A 矩陣的 rows 的個數。
- n: A 矩陣的 columns 的個數。
- A: A 矩陣。函式結束時,會將 L 和 U 存在這裡。
- LDA: 至少要是 m。
- ipiv: 整數陣列,會把 row 交換的過程存下來,第 i 個 row 會和第 ipiv(i) 個 row 交換。
- info: 整數,代表計算結果。
- 0: 成功。
- <0: 值為 -i 時代表第 i 個參數有非法值。
- >0: 值為 -i 時代表 U(i,i) 為 0。
完整程式如下:
123456789101112131415161718192021222324252627282930313233343536373839404142#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <f2c.h>
#include <clapack.h>
void
matrixDisplay
(
double
mat[]
,
int
m
,
int
n
)
{
int
i
,
j;
for
(
i=0;i<m;i++
)
{
for
(
j=0;j<n;j++
)
{
printf
(
"%.0f "
,
mat[i*n+j]
)
;
}
printf
(
"\n"
)
;
}
}
int
main
(
)
{
int
n
,
*ipiv;
double
*A;
int
i
,
j
,
info;
/* space allocation */
n = 3;
A =
(
double
*
)
malloc
(
n*n*
sizeof
(
double
)
)
;
ipiv =
(
int
*
)
malloc
(
n*
sizeof
(
int
)
)
;
/* value generation/initialization */
srand
(
time
(
NULL
)
)
;
j = n*n;
for
(
i=0; i<j; i++
)
{
A[i] =
rand
(
)
%5+1;
}
matrixDisplay
(
A
,
n
,
n
)
;
dgetrf_
(
&n
,
&n
,
A
,
&n
,
ipiv
,
&info
)
;
matrixDisplay
(
A
,
n
,
n
)
;
free
(
A
)
;
return
0;
}