1. 程式人生 > >MKL學習——基本操作C++實現

MKL學習——基本操作C++實現

前言

前面介紹了各種向量-向量,矩陣-向量,矩陣-矩陣的函式簡介。根據自身目前狀況,主要使用實數域的操作,也就是說關注單精度float型別的s和雙精度double型別的d。還有就是用的基本都是全矩陣,沒有經過壓縮,也不是對稱、三角、帶狀的某一種情況。所以主要還是總結一般的乘法、加法操作。

【注】程式碼都以單精度float的情況書寫,主要流程要記住,使用mkl_malloc申請記憶體,使用mkl_free釋放記憶體。n年沒用過C++了,湊合看看吧。

學MKL的肯定對程式設計有一定程度瞭解,智慧提示是一個很好的工具,在VS中,輸入cblas_s以後,會自動補全所有的單精度操作函式,那麼根據常規經驗,就能判斷出它到底用於做什麼以及需要的引數;比如提示axpby

意思就是a*xb*y。還有就是函式有兩種,一種是有返回值,一種無返回值,怎麼辦,只能提示看函式的宣告是void還是float或者是double型別即可。

向量-向量

加法

運算

y=ax+by
程式碼

#include<stdio.h>
#include<stdlib.h>
#include<mkl.h>
int main()
{
    float *A, *B;//兩個向量
    int a=1, b=1;//標量
    int n = 5;//向量大小
    A = (float *)mkl_malloc(n * 1 * sizeof
(float), 64); B = (float *)mkl_malloc(n * 1 * sizeof(float), 64); printf("The 1st vector is "); for (int i = 0; i < n; i++){ A[i] = i; printf("%2.0f", A[i]); } printf("\n"); printf("The 2st vector is "); for (int i = 0; i < n; i++){ B[i] =i+1; printf
("%2.0f", B[i]); } printf("\n"); //計算a*A+b*B cblas_saxpby(n, a, A, 1, b, B, 1); printf("The a*A+b*B is "); for (int i = 0; i < n; i++){ printf("%2.0f", B[i]); } printf("\n"); mkl_free(A); mkl_free(B); getchar(); return 0; }

結果

The 1st vector is  0 1 2 3 4
The 2st vector is  1 2 3 4 5
The a*A+b*B is  1 3 5 7 9

乘法

運算:向量點乘

程式碼:

//乘法
#include<stdio.h>
#include<stdlib.h>
#include <mkl.h>

int main()
{
    float *A, *B;//兩個向量
    int a = 1, b = 1;//標量
    int n = 5;//向量大小
    float res;
    A = (float *)mkl_malloc(n * 1 * sizeof(float), 64);
    B = (float *)mkl_malloc(n * 1 * sizeof(float), 64);
    printf("The 1st vector is ");
    for (int i = 0; i < n; i++){
        A[i] = i;
        printf("%2.0f", A[i]);
    }
    printf("\n");
    printf("The 2st vector is ");
    for (int i = 0; i < n; i++){
        B[i] = i + 1;
        printf("%2.0f", B[i]);
    }
    printf("\n");
    //乘法:對應元素乘積的加和
    res=cblas_sdot(n, A, 1, B, 1);
    printf("點乘結果: %2.0f",res);
    printf("\n");
    mkl_free(A);
    mkl_free(B);
    getchar();
    return 0;
}

結果:

The 1st vector is  0 1 2 3 4
The 2st vector is  1 2 3 4 5
點乘結果: 40

二範數

運算:二範數或者歐幾里得範數,是所有元素平方和開根號

程式碼

//計算向量二範數
#include<stdio.h>
#include<stdlib.h>
#include<mkl.h>
void main()
{
    float *A;
    int n = 5;
    float res;
    A = (float *)mkl_malloc(n*sizeof(float), 64);
    printf("The original vector:\n");
    for (int i = 0; i < n; i++)
    {
        A[i] = i + 1;
        printf("%2.0f ", A[i]);
    }
    printf("\n");
    res = cblas_snrm2(n, A, 1);//計算二範數
    printf("The norm2 of vector is:%2.6f", res);
    mkl_free(A);
    getchar();
}

結果:

The original vector1  2  3  4  5
The norm2 of vector is:7.416198

旋轉

運算:將空間中一個點,繞原點旋轉的角度

程式碼:以二維座標點(2,0)繞原點旋轉45°為例。程式碼有點問題,一釋放記憶體就出錯,具體原因是對兩個向量開闢空間以後又讓它們指向了別的地址,造成了開闢空間無用。所以呼叫Cblas函式,可以直接把指向陣列的指標丟進去。暫時先這樣理解吧,等把C++複習一遍再來看看分析的對不對。

//旋轉,以二維空間中的一個點(2,0)繞原點旋轉45°
#include<stdio.h>
#include<stdlib.h>
#include<mkl.h>
#include<math.h>
#define M_PI 3.14159265358979323846

int main()
{
    float *A, *B;//A是座標點,B是旋轉矩陣
    float point1[] = { 2 };//旋轉點x座標
    float point2[] = { 0 };//旋轉點y座標
    float rotpoint[] = { cos(45.0*M_PI / 180), sin(45.0*M_PI / 180) };
    //A = (float *)mkl_malloc(1 * sizeof(float), 64);
    //B = (float *)mkl_malloc(1 * sizeof(float), 64);
    A = point1;
    B = point2;
    printf("The point is (%2.0f,%2.0f)",point1[0],point2[0]);
    printf("\n");
    //計算旋轉後的點
    cblas_srot(1, A, 1, B, 1, rotpoint[0], rotpoint[1]);
    printf("The rotated is (%2.6f,%2.6f)", A[0], B[0]);
    printf("\n");
    //mkl_free(A);
    //mkl_free(B);
    getchar();
    return 0;
}

結果:還是比較正確的

The point is ( 2, 0)
The rotated is (1.414214,-1.414214)

縮放

運算

x=ax
程式碼
//計算向量縮放
#include<stdio.h>
#include<stdlib.h>
#include<mkl.h>
void main()
{
    float *A;
    int n = 5;
    float scal=0.1;
    A = (float *)mkl_malloc(n*sizeof(float), 64);
    printf("The original vector:\n");
    for (int i = 0; i < n; i++)
    {
        A[i] = i + 1;
        printf("%2.0f ", A[i]);
    }
    printf("\n");
    cblas_sscal(n, scal, A, 1);//縮放
    printf("The scaled vector:\n");
    for (int i = 0; i < n; i++)
    {
        printf("%2.1f ", A[i]);
    }
    mkl_free(A);
    getchar();
}

結果

The original vector1  2  3  4  5
The scaled vector0.1 0.2 0.3 0.4 0.5

交換

運算:交換兩個向量

程式碼

//交換
#include<stdio.h>
#include<stdlib.h>
#include<mkl.h>

int main()
{
    float *A, *B;//兩個向量
    int a = 1, b = 1;//標量
    int n = 5;//向量大小
    A = (float *)mkl_malloc(n * 1 * sizeof(float), 64);
    B = (float *)mkl_malloc(n * 1 * sizeof(float), 64);
    printf("The 1st vector is ");
    for (int i = 0; i < n; i++){
        A[i] = i;
        printf("%2.0f", A[i]);
    }
    printf("\n");
    printf("The 2st vector is ");
    for (int i = 0; i < n; i++){
        B[i] = i + 1;
        printf("%2.0f", B[i]);
    }
    printf("\n");
    //交換AB
    cblas_sswap(n, A, 1, B, 1);
    printf("The 1st swapped vctor is");
    for (int i = 0; i < n; i++){
        printf("%2.0f", A[i]);
    }
    printf("\n");
    printf("The 2st swapped vctor is");
    for (int i = 0; i < n; i++){
        printf("%2.0f", B[i]);
    }
    printf("\n");
    mkl_free(A);
    mkl_free(B);
    getchar();
    return 0;
}

結果

The 1st vector is  0 1 2 3 4
The 2st vector is  1 2 3 4 5
The 1st swapped vctor is 1 2 3 4 5
The 2st swapped vctor is 0 1 2 3 4

最值

運算:求最大最小值

程式碼

//求最大最小值
#include<stdlib.h>
#include<stdio.h>
#include<mkl.h>
void main()
{
    float *A;//向量
    int n = 5;//向量大小
    int max, min;
    A = (float *)mkl_malloc(n * 1 * sizeof(float), 64);
    printf("The 1st vector is ");
    for (int i = 0; i < n; i++){
        A[i] = i;
        printf("%2.0f", A[i]);
    }
    printf("\n");
    //計算最值位置
    max=cblas_isamax(n, A, 1);
    min = cblas_isamin(n, A, 1);
    printf("The max value is %2.0f, position is %d\n", A[max], max + 1);
    printf("The min value is %2.0f, position is %d\n", A[min], min + 1);
    mkl_free(A);
    getchar();
}

結果

The 1st vector is  0 1 2 3 4
The max value is  4, position is 5
The min value is  0, position is 1

矩陣-向量

乘法1

運算:

y:=αAx+βy
程式碼
//矩陣-向量乘積
#include<stdio.h>
#include<stdlib.h>
#include<mkl.h>
int main()
{
    float *A, *B,*C;//A是矩陣,B是向量,C是向量
    int m = 2;//矩陣行數
    int n = 5;//向量維度,矩陣列數
    int a = 1, b = 1;//縮放因子

    A = (float *)mkl_malloc(m*n*sizeof(float), 64);
    B = (float *)mkl_malloc(n*sizeof(float), 64);
    C = (float *)mkl_malloc(m*sizeof(float), 64);
    //賦值,按行儲存?
    printf("陣列為\n");
    for (int i = 0; i < m*n; i++){
        if (i%n == 0 && i != 0)
            printf("\n");

        A[i] = i;
        printf("%2.0f",A[i]);
    }   
    printf("\n");
    printf("向量為\n");
    for (int i = 0; i < n; i++)
    {
        B[i] = i + 1;
        printf("%2.0f", B[i]);
    }
    printf("\n");
    for (int i = 0; i < m*n; i++)
        C[i] = 0;
    //2*5的矩陣與5*1的向量相乘
    cblas_sgemv(CblasRowMajor, CblasNoTrans, m, n, a, A, n, B, 1, b, C, 1);

    printf("矩陣-向量乘法結果\n");
    for (int i = 0; i < m; i++){
        printf("%2.0f ", C[i]);
    }
    mkl_free(A);
    mkl_free(B);
    mkl_free(C);
    getchar();
    return 0;
}

結果

陣列為
 0 1 2 3 4
 5 6 7 8 9
向量為
 1 2 3 4 5
矩陣-向量乘法結果
40 115

乘法2

運算

A:=αxy+A,
程式碼
//矩陣-向量乘積
#include<stdio.h>
#include<stdlib.h>
#include<mkl.h>
int main()
{
    float *A, *B, *C;//A是矩陣,B是向量
    int m=2,n = 5;//B,C向量維度
    int a = 1;//縮放因子

    A = (float *)mkl_malloc(m*n*sizeof(float), 64);
    B = (float *)mkl_malloc(m*sizeof(float), 64);
    C = (float *)mkl_malloc(n*sizeof(float), 64);
    //賦值,按行儲存?
    printf("陣列為\n");
    for (int i = 0; i < m*n; i++){
        if (i%n == 0 && i != 0)
            printf("\n");

        A[i] = 1;
        printf("%2.0f", A[i]);
    }
    printf("\n");
    printf("向量1為\n");
    for (int i = 0; i < m; i++)
    {
        B[i] = i + 1;
        printf("%2.0f", B[i]);
    }
    printf("\n");
    printf("向量2為\n");
    for (int i = 0; i < n; i++)
    {
        C[i] = i+2;
        printf("%2.0f", C[i]);
    }
    printf("\n");
    //5*1向量乘以1*5向量,加上5*5矩陣
    cblas_sger(CblasRowMajor, m, n, a, B, 1, C, 1, A, n);

    printf("向量-向量相乘+矩陣的結果\n");
    for (int i = 0; i < m*n; i++){
        if (i%n == 0 && i != 0)
            printf("\n");

        printf("%2.0f ", A[i]);
    }
    mkl_free(A);
    mkl_free(B);
    mkl_free(C);
    getchar();
    return 0;
}

結果

陣列為
 1 1 1 1 1
 1 1 1 1 1
向量1為
 1 2
向量2為
 2 3 4 5 6
向量-向量相乘+矩陣的結果
 3  4  5  6  7
 5  7  9 11 13

矩陣-矩陣

乘法1

運算

C:=αop(A)op(B)+βC,
程式碼
#include<stdio.h>
#include<stdlib.h>
#include<mkl.h>
int main()
{
    float *A, *B, *C;
    int m = 2, n = 3, k = 2;//A維度2*3,B維度2*3(計算時候轉置),C維度2*2
    int a = 1, b = 1;//縮放因子
    A = (float *)mkl_malloc(m*n*sizeof(float), 64);
    B = (float *)mkl_malloc(n*k*sizeof(float), 64);
    C = (float *)mkl_malloc(m*k*sizeof(float), 64);

    printf("矩陣1為\n");
    for (int i = 0; i < m*n; i++)
    {
        if (i != 0 && i%n == 0)
            printf("\n");
        A[i] = i + 1;
        printf("%2.0f", A[i]);
    }
    printf("\n");

    printf("矩陣2為\n");
    for (int i = 0; i < n*k; i++)
    {
        if (i != 0 && i%k == 0)
            printf("\n");
        B[i] = 1;
        printf("%2.0f", B[i]);
    }
    printf("\n");

    printf("矩陣3為\n");
    for (int i = 0; i < m*k; i++)
    {
        if (i != 0 && i%k == 0)
            printf("\n");
        C[i] = i;
        printf("%2.0f", C[i]);
    }
    printf("\n");

    printf("結果矩陣\n");
    cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, m, k, n, a, A, n, B, k, b, C, k);//注意mkn的順序☆
    for (int i = 0; i < m*k; i++)
    {
        if (i != 0 && i%k == 0)
            printf("\n");
        printf("%2.0f", C[i]);
    }
    printf("\n");

    mkl_free(A);
    mkl_free(B);
    mkl_free(C);
    getchar();
    return 0;
}

結果

矩陣1為
 1 2 3
 4 5 6
矩陣2為
 1 1
 1 1
 1 1
矩陣3為
 0 1
 2 3
結果矩陣
 6 7
1718

乘法2

依舊是上述的功能,此處嘗試一下

#include<stdio.h>
#include<stdlib.h>
#include<mkl.h>
int main()
{
    float *A, *B, *C;
    int m = 2, n = 3, k = 2;//A維度2*3,B維度2*3(計算時候轉置),C維度2*2
    int a = 1, b = 1;//縮放因子
    A = (float *)mkl_malloc(m*n*sizeof(float), 64);
    B = (float *)mkl_malloc(k*n*sizeof(float), 64);
    C = (float *)mkl_malloc(m*k*sizeof(float), 64);

    printf("矩陣1為\n");
    for (int i = 0; i < m*n; i++)
    {
        if (i != 0 && i%n == 0)
            printf("\n");
        A[i] = i + 1;
        printf("%2.0f", A[i]);
    }
    printf("\n");

    printf("矩陣2為\n");
    for (int i = 0; i < k*n; i++)
    {
        if (i != 0 && i%n == 0)
            printf("\n");
        B[i] = 1;
        printf("%2.0f", B[i]);
    }
    printf("\n");

    printf("矩陣3為\n");
    for (int i = 0; i < m*k; i++)
    {
        if (i != 0 && i%k == 0)
            printf("\n");
        C[i] = i;
        printf("%2.0f", C[i]);
    }
    printf("\n");

    printf("結果矩陣\n");

    cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, m, k, n, a, A, n, B, n, b, C, k);//注意mkn的順序☆
    for (int i = 0; i < m*k; i++)
    {
        if (i != 0 && i%k == 0)
            printf("\n");
        printf("%2.0f", C[i]);
    }
    printf("\n");

    mkl_free(A);
    mkl_free(B);
    mkl_free(C);
    getchar();
    return 0;

}

結果

矩陣1為
 1 2 3
 4 5 6
矩陣2為
 1 1 1
 1 1 1
矩陣3為
 0 1
 2 3
結果矩陣
 6 7
1718

注意點

最主要的是記住引數順序,首先是矩陣op(A)和的C行數,op代表操作,乘法2中的op就是轉置;然後是矩陣op(B)C的列數;隨後才是op(A)的列數和op(B)的行數。對於乘法2中的第二個矩陣,也就是B矩陣,雖然轉置了,但是還是以不轉置時候,以行儲存方式的引導維度,也就是列數為輸入引數。
而且丟入函式的引數,並不一定是mkl_malloc開闢的空間,也可以是其它陣列,用指標指向陣列地址,然後丟到函式就行了。
奉上code(vs2013): MKL -C++基本操作