SuiteSparseQR_C への道・その4 | ぽんのブログ

ぽんのブログ

自分用の備忘録ブログです。書いてある内容、とくにソースは、後で自分で要点が分かるよう、かなり簡略化してます(というか、いい加減)。あまり信用しないように(汗

前回は cholmod_triplet について書きましたが、今回は疎行列 cholmod_sparse について。

SuiteSparseでは、疎行列を表現するために cholmod_sparse を使います。
その定義は以下のようになります。

--- CHOLMOD/Include/cholmod_core.h 1116行 ---
typedef struct cholmod_sparse_struct
{
    size_t nrow;    // 行数
    size_t ncol;     // 列数
    size_t nzmax ; // 行列要素の最大数

    void *p;          // 各列までの非ゼロ成分の総数。 long * (ncol + 1)
    void *i;           // 非ゼロ成分の行(row)番号。 long * nzmax

    int packed;      // 行列を圧縮して格納するか否か
    void *nz;         // 各列内の非ゼロ成分数(packed == TRUE の場合のみ)。long * ncol

    void *x;          // データ
    void *z;          // 複素数の場合用

    int stype;      // 前回の記事参照
    int itype;
    int xtype;
    int dtype;

    int sorted;     // 列についてソートするか否か

} cholmod_sparse;

cholmod_sparse オブジェクトのメモリ確保をするのが以下の関数

cholmod_sparse *
cholmod_l_allocate_sparse (size_t nrow, size_t ncol, size_t nzmax,
                                        int sorted, int packed, int stype, int xtype,
                                        cholmod_common *cc);
です。

この関数で、
A->i, A->x (及び複素数の場合 A->z ) に nzmax個の long*、double*の配列、
A->p に ncol + 1個のlong*の配列が確保されます。
引数 packed == FALSE なら A->nz に ncol 個のlong*が確保されます(packed == TRUE なら A->nz = NULL)。
また A->p、A->nz の各要素には 0 が代入され初期化されます。

A->i、A->x のそれぞれには非ゼロ成分の行番号、値が格納されます。

例えば

A = 0     2.2     3.3
     1. 1    0      4.4

の場合、

A->i[0] = 1,  A->x[0] = 1.1
A->i[1] = 0,  A->x[1] = 2.2
A->i[2] = 0,  A->x[2] = 3.3
A->i[3] = 1,  A->x[3] = 4.4

となります。

A->p には、p[0] = 0として、各列までに何個の非ゼロ要素があるか、その総数が格納されます。
上の例の場合

一列目の最後までの非ゼロ成分の総数は1個(非ゼロ成分数は1個)、
二列目は2個(非ゼロ成分数は1個)、
三列目は4個(非ゼロ成分数は2個)

となっているので、

A->p[0] = 0
A->p[1] = 1
A->p[2] = 2
A->p[3] = 4

となります。

さらに5番目の引数 packed = FALSE を指定すると、A->nz に各列内の非ゼロ成分数が格納されます。
上の例なら

A->nz[0] = 1  // 1列目
A->nz[1] = 1  // 2列目
A->nz[2] = 2  // 3列目

となります。


以下の例では、先にあげた行列

A =
    0     2.2   3.3
    1.1   0     4.4

を cholmod_sparse* に格納し出力します。

#include <cholmod.h>

int
main (void)
{
    cholmod_sparse   *A;
    long    *Ai;
    long    *Ap;
    long    *Anz;
    double *Ax;

    int    sorted;
    int    packed;
    int    stype;

    cholmod_common   Common, *cc;

    cc = &Common;
    cholmod_l_start (cc);

    sorted = TRUE;
    packed = FALSE;
    stype = 0;

    A = cholmod_l_allocate_sparse (2, 3, 4, sorted, packed,
                                                   stype, CHOLMOD_REAL, cc);
    Ai = (long *) A->i;
    Ap = (long *) A->p;
    if (!packed) Anz = (long *) A->nz;
    Ax = (double *) A->x;

    Ai[0] = 1;  Ax[0] = 1.1;   // 1列目: 2行目の非ゼロの値が 1.1
    Ap[1] = 1;
    if (!packed) Anz[0] = 1;  // 1列目には非ゼロは1個

    Ai[1] = 0;  Ax[1] = 2.2;   // 2列目: 1列目の非ゼロの値が 2.2
    Ap[2] = 2;
    if (!packed) Anz[1] = 1;  // 2列目には非ゼロは1個

    Ai[2] = 0;  Ax[2] = 3.3;   // 3列目: 1列目の非ゼロの値が 3.3
    Ai[3] = 1;  Ax[3] = 4.4;   //           2列目の非ゼロの値が 4.4
    Ap[3] = 4;
    if (!packed) Anz[2] = 2;  // 3列目には非ゼロは2個

    cholmod_l_write_sparse (stdout, A, NULL, NULL, cc);

    cholmod_l_free_sparse (&A, cc);
    cholmod_l_finish (cc);

    return 0;
}

$ gcc -g -o hoge hoge.c -I/usr/include/suitesparse -lcholmod

でコンパイルして実行した結果は

%%MatrixMarket matrix coordinate integer general
2 3 4
2 1 1.1
1 2 2.2
1 3 3.3
2 3 4.4

と、前回と同じ結果になりました。