稀疏矩陣概念及簡單實現

稀疏矩陣:

在矩陣中,若數值爲0的元素數目遠遠多於非0元素的數目,並且非0元素分佈沒有規律時,則稱該矩陣爲稀疏矩陣;與之相反,若非0元素數目佔大多數時,則稱該矩陣爲稠密矩陣。定義非零元素的總數比上矩陣所有元素的總數爲矩陣的稠密度。

這裏寫圖片描述

特性:
1.稀疏矩陣其非零元素的個數遠遠小於零元素的個數,而且這些非零元素的分佈也沒有規律。
2.稀疏因子是用於描述稀疏矩陣的非零元素的比例情況。設一個n*m的稀疏矩陣A中有t個非零元素,則稀疏因子δδ的計算公式如下:δ=tn∗mδ=tn∗m(當這個值小於等於0.05時,可以認爲是稀疏矩陣)

矩陣壓縮:

存儲矩陣的一般方法是採用二維數組,其優點是可以隨機地訪問每一個元素,因而能夠較容易地實現矩陣的各種運算,如轉置運算、加法運算、乘法運算等。
對於稀疏矩陣來說,採用二維數組的存儲方法既浪費大量的存儲單元用來存放零元素,又要在運算中花費大量的時間來進行零元素的無效計算。所以必須考慮對稀疏矩陣進行壓縮存儲。

最常用的稀疏矩陣存儲格式主要有:COO(Coordinate Format)和CSR(Compressed Sparse Row)。

COO很簡單,就是使用3個數組,分別存儲全部非零元的行下標(row index)、列下標(column index)和值(value);CSR稍複雜,對行下標進行了壓縮,假設矩陣行數是m,則壓縮後的數組長度爲m+1,記作(row ptr),其中第i個元素(0-base)表示矩陣前i行的非零元個數。

(1)Coordinate(COO)
這裏寫圖片描述
這是最簡單的一種格式,每一個元素需要用一個三元組來表示,分別是(行號,列號,數值),對應上圖右邊的一列。這種方式簡單,但是記錄單信息多(行列),每個三元組自己可以定位,因此空間不是最優。

(2)Compressed Sparse Row (CSR)
這裏寫圖片描述
CSR是比較標準的一種,也需要三類數據來表達:數值,列號,以及行偏移。CSR不是三元組,而是整體的編碼方式。數值和列號與COO一致,表示一個元素以及其列號,行偏移表示某一行的第一個元素在values裏面的起始偏移位置。如上圖中,第一行元素1是0偏移,第二行元素2是2偏移,第三行元素5是4偏移,第4行元素6是7偏移。在行偏移的最後補上矩陣總的元素個數,本例中是9。

CSC是和CSR相對應的一種方式,即按列壓縮的意思。
以上圖中矩陣爲例:
Values: [1 5 7 2 6 8 3 9 4]
Row Indices:[0 2 0 1 3 1 2 2 3]
Column Offsets:[0 2 4 7 9]

其他的存儲格式如:CSC,DIA,ELL,HYB等參考博文
http://blog.csdn.net/gggg_ggg/article/details/47402459

稀疏矩陣的實現:

/*
 * @describe: sparse matrix
 * @date: 2018/02/28
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "SparseMatrix.h"
#include "common.h"
#include "MyTime.h"


//coo轉化爲csr矩陣
void coo_to_csr_sparse_matrix(COOSparseMatrix *cm, CSRSparseMatrix *sm)
{
        unsigned int i, j;
        unsigned int tot = 0;
        int nz, nr;
        nz = cm->NonZeroNum;
        nr = cm->rowNum + 1;
        // Init mem for CSR matrix
        sm->val = (double *)malloc(nz * sizeof(double));
        sm->col = (unsigned int *)malloc(nz * sizeof(unsigned int));
        sm->ptr = (unsigned int *)malloc(nr * sizeof(unsigned int));
        memset(sm->ptr, 0, nr * sizeof(unsigned int));
        sm->NonZeroNum = cm->NonZeroNum;
        sm->rowNum = cm->rowNum;
        sm->colNum = cm->colNum;

        sm->ptr[0] = tot;
        for (i = 0; i < sm->rowNum; i++){
                for (j = 0; j < cm->NonZeroNum; j++){
                        if (cm->row[j] == i){
                                sm->val[tot] = cm->val[j];
                                sm->col[tot] = cm->col[j];
                                tot++;
                                sm->ptr[i + 1] = tot;
                        }
                }

        }

}

//創建coo矩陣
void create_coo_sparse_matrix(COOSparseMatrix *cm)
{
        int i,nz;
        int m, n;
        double t;
        FILE *fp;
        nz = cm->NonZeroNum;
        printf("len:%d\n", nz);

        cm->val = (double *)malloc(nz * sizeof(double));
        cm->row = (unsigned int *)malloc(nz * sizeof(unsigned int));
        cm->col = (unsigned int *)malloc(nz * sizeof(unsigned int));
        m = n = 0;
        t = 0.0;
        fp = fopen(FILE_PATH, "r");
        if (fp == NULL){
                printf("file does not exist");
                return;
        }


        for (i = 0; i < nz; i++){
                fscanf(fp, "%d %d %lf\n", &m, &n, &t);
                //printf("row:%d, col:%d, val:%lf\n", m, n, t);
                cm->row[i] = m;
                cm->col[i] = n;
                cm->val[i] = t;
        }

        fclose(fp);
}

//刪除矩陣
void free_csr_sparse_matrix(CSRSparseMatrix *coo)
{       
        if (coo->col != NULL)
                free(coo->col);
        if (coo->ptr != NULL)
                free(coo->ptr);
        if (coo->val != NULL)
                free(coo->val);

        coo->col = NULL;
        coo->ptr = NULL;
        coo->val = NULL;
        coo->colNum = coo->rowNum = coo->NonZeroNum = 0;
}

void free_coo_sparse_matrix(COOSparseMatrix *coo)
{
        if (coo->row != NULL)
                free(coo->row);
        if (coo->col != NULL)
                free(coo->col);
        if (coo->val != NULL)
                free(coo->val);

        coo->row = NULL;
        coo->col = NULL;
        coo->val = NULL;
        coo->colNum = coo->rowNum = coo->NonZeroNum = 0;

}

//矩陣加法
int SparseMatrixAdd(CSRSparseMatrix sm1, CSRSparseMatrix sm2, CSRSparseMatrix *output)
{
        if (sm1.colNum != sm2.colNum || sm1.rowNum != sm2.rowNum)
                return ERR_FAILED;

        //to be defined
        return ERR_OK;
}

//y = mx
void csr_SPMV(CSRSparseMatrix *csr, double *x, double *y)
{
        unsigned int i, j, end;
        double sum;
        double *val = csr->val;
        unsigned int *col = csr->col;
        unsigned int *ptr = csr->ptr;
        end = 0;

        // Loop over rows.
        for (i = 0; i < csr->rowNum; i++){
                sum = 0.0;
                j = end;
                end = ptr[i + 1];
                // Loop over non-zero elements in row i.
                for (; j < end; j++){
                        sum += val[j] * x[col[j]];
                }
                y[i] = sum;
        }
}


void display_csr_matrix(CSRSparseMatrix *sm)
{
        unsigned int i,j;
        printf("val= ");
        for (i = 0; i < sm->NonZeroNum; i++){
                printf("%.2g | ", sm->val[i]);
        }
        j = 1;
        printf("\ncol= ");
        for (i = 0; i < sm->NonZeroNum; i++){
                printf("%d | ", sm->col[i]);
        }
        printf("\nptr= ");
        for(i=0; i < sm->rowNum + 1; i++){
                if (sm->ptr[i] != 0 || i==0)
                        printf("%d | ", sm->ptr[i]);
        }
        printf("\n");
        //printf("\tnz= %d\n", m->nz);
        //printf("\trows= %d\n", m->rows);
        //printf("\tcols= %d\n", m->cols);
}

void create_random_data(int len)
{       
        int m;
        int n;
        double a;
        int i;
        FILE *fp;

        fp = fopen(FILE_PATH, "w+");
        if (fp == NULL){
                printf("open file failed!\n");
                return;
        }
        printf("len is %d\n",len);
        srand((unsigned)time(NULL));
        for (i = 0; i < len; i++){
                /*create random data*/
                m = rand() % 100;
                n = rand() % 100;
                a = rand() / (double)(RAND_MAX);
                fprintf(fp, "%d %d %lf\n", m, n, a);
        }

        fclose(fp);
}

int main(int argc, char *argv[])
{       

        double *x;
        double *y;
        int m_rows = 100;
        int m_cols = 100;
        int n_zeros = 9;
        int loops = 1000;
        MyTime mytime;
        int i;

        COOSparseMatrix coo;
        CSRSparseMatrix csr;

        LINEPRINT;

        //create_random_data(n_zeros);
        if (strcmp("create", argv[1]) == 0){
                printf("now create random data!\n");
                create_random_data(n_zeros);        
        }

        coo.rowNum = m_rows;
        coo.NonZeroNum = n_zeros;
        coo.colNum = m_cols;
        create_coo_sparse_matrix(&coo);
        coo_to_csr_sparse_matrix(&coo, &csr);
        display_csr_matrix(&csr);
        free_coo_sparse_matrix(&coo);
        x = (double *)malloc(m_cols *sizeof(double));
        y = (double *)malloc(m_rows *sizeof(double));


        for (i = 0; i < csr.colNum; i++){
                x[i] = i + 1;
        }

        //計算y = mx的時間
        time_start(&mytime);
        for (i = 0; i < loops; i++){
                csr_SPMV(&csr, x, y);
        }
        time_stop(&mytime);
        printf("smmv use time:%lf\n", mytime.sec);


        free_csr_sparse_matrix(&csr);
        free(x);
        free(y);
        x = NULL;
        y = NULL;
        exit(EXIT_SUCCESS);
        return 0;



}

主要實現了coo創建稀疏矩陣,轉化爲csr稀疏矩陣,以及稀疏矩陣的矢量乘法

結果:
這裏寫圖片描述

稀疏矩陣的優化:

最近考慮稀疏矩陣的優化,但是看不到MKL的實現,想了幾個思路,不知道對稀疏矩陣的優化有沒有幫助,暫時先記下來。
1.多線程。使用openmp或者mpi
2.numanode awareness 特性。把稀疏矩陣的存儲均勻地分配到兩顆處理器各自的本地內存中,最大程度的利用內存帶寬
3.利用硬件cache特性,對矩陣進行分塊或矩陣的循環進行限制
4.利用pipeline,多流水線並行處理
5.自適應分塊存儲結構。由於稀疏矩陣的非零元分佈不一定均勻,有的分塊會非常稀疏,有的則會相對稠密。對於極稀疏的分塊(非零元數量遠小於行數),如果用和CSR相似的壓縮行存儲策略,則會浪費空間,所以用COO的方式反而更能節省存儲空間,提高訪問效率。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章