BLAS方法简介(一)

BLAS(Basic Linear Algebra Subprograms 基础线性代数程序集)相关算法中文的介绍比较少,所以写一篇简单记录下,难免有误,欢迎拍砖。

BLAS按照功能被分为三个级别:
Level 1:矢量-矢量运算
$y \leftarrow \alpha x + y$
Level 2:矩阵-矢量运算
$y \leftarrow \alpha Ax + \beta y$
Level 3:矩阵-矩阵运算
$C \leftarrow \alpha AB + \beta C$
官方参考实现:http://www.netlib.org/blas/
代码示例选用com.github.fommil.netlib.BLAS(spark官方亦是这个)

本文介绍下Level1

srot

实现数组a,b间数据的处理,处理公式为:
$a_i = c \ast a_i + s \ast b_i$
$b_i = c \ast b_i - s \ast a_i$

1
2
3
4
5
6
7
public void srot(int n, float[] sx, int _sx_offset, int incx, float[] sy, int _sy_offset, int incy, float c, float s);
参数解析:
n :需要处理的数据总数
sx|sy :互相处理数据的数组
_sx_offset|_sy_offset :sx|sy数组数据处理起始的位置
incx|incy :步长,取数据的间隔
c|s :数据处理的系数

1
2
3
4
5
6
7
8
9
val a = Array(2.0f,3.0f,5.0f,4.0f,1.0f)
val b = Array(2.0f,3.0f,5.0f,4.0f,1.0f)

val e = blas.srot(2,a,1,1,b,0,2,3.0f,2.0f)

println(a.mkString(","))
println(b.mkString(","))
//2.0,13.0,25.0,4.0,1.0
//0.0,3.0,5.0,4.0,1.0

本例中需要处理的数据数量为2,a数组从索引1开始,以步长为1取数据进行处理,也就是取到(3.0,5.0),b数组从索引0开始,以步长为2取数据进行处理,也就是取到(2.0,5.0),那么也就是对这四个数据进行处理,怎么处理呢?
按照公式$a_i = c \ast a_i + s \ast b_i$,$b_i = c \ast b_i - s \ast a_i$
$a_1$ = 3.0 $\ast$ 3+2.0 $\ast$ 2 = 13.0
$a_2$ = 5.0 $\ast$ 3+5.0 $\ast$ 2 = 25.0
$b_0$ = 2.0$\ast$3-3.0$\ast$2 = 0.0
$b_2$ = 5.0$\ast$3-5.0$\ast$2 = 5.0

sscal

实现数组内数据的乘法
$sx_i = sx_i \ast sa$

1
2
3
4
5
6
public void sscal(int n, float sa, float[] sx, int _sx_offset, int incx);
参数解析:
n :需要处理的数据总数
sx :需要处理数据的数组
_sx_offset :sx数组数据处理起始的位置
incx :步长,取数据的间隔

1
2
3
4
5
val a = Array(1.0f,2.0f,3.0f,4.0f,5.0f)
blas.sscal(2,2.0f,a,0,1)

println(a.mkString(","))
//2.0,4.0,3.0,4.0,5.0

以步长1从索引0开始取两个数据,也即(1,2),进行乘法,乘法的系数是2.0,所以结果就是(2,4)

saxpy

数组处理,处理公式为:$y=a \ast x+y$

1
2
3
4
5
6
7
public void saxpy(int n, float sa, float[] sx, int _sx_offset, int incx, float[] sy, int _sy_offset, int incy);
参数解析:
n :需要处理的数据总数
sa :数据处理的系数
sx|sy :互相处理数据的数组
_sx_offset|_sy_offset :sx|sy数组数据处理起始的位置
incx|incy :步长,取数据的间隔

1
2
3
4
5
6
7
8
9
val a = Array(1.0f,2.0f,3.0f,4.0f,5.0f)
val b = Array(1.0f,2.0f,3.0f,4.0f,5.0f)

blas.saxpy(2,2.0f,a,0,1,b,1,1)

println(a.mkString(","))
println(b.mkString(","))
//1.0,2.0,3.0,4.0,5.0
//1.0,4.0,7.0,4.0,5.0

从索引0,以步长1从a取2个数字,也即(1.0,2.0),从索引1,以步长1从b取2个数字,也即(2.0,3.0),执行计算(1.0$\ast$2.0+2.0=4.0,2.0$\ast$2.0+3.0=7.0),放置到刚才b取出数据的位置即为结果

sswap

实现数组内数据的交换

1
2
3
4
5
6
public void sswap(int n, float[] sx, int _sx_offset, int incx, float[] sy, int _sy_offset, int incy);
参数解析:
n :需要交换的数据总数
sx|sy :互相交换数据的数组
_sx_offset|_sy_offset :sx|sy数组数据交换起始的位置
incx|incy :步长,取数据的间隔

1
2
3
4
5
6
7
8
val a =  Array(1.0f,3.0f,5.0f,2.0f,4.0f)
val b = Array(1.0f,3.0f,5.0f,2.0f,4.0f)
blas.sswap(2,a,1,1,b,2,2)

println(a.mkString(","))
println(b.mkString(","))
//1.0,5.0,4.0,2.0,4.0
//1.0,3.0,3.0,2.0,5.0

a数组从索引1开始取,步长为1,取俩,也就是(3.0,5.0)
b数组从索引2开始取,步长为2,取俩,也就是(5.0,4.0)
ab取出来的数据互相交换位置即为结果

dasum

实现数组内数据的累加

1
2
3
4
5
6
public double dasum(int n, double[] dx, int _dx_offset, int incx)
参数解析:
n :表示总共取多少数据累加,取0的话结果为0
dx :执行累加的数据集
_dx_offset :累加第一个数据在数组dx中的index
incx :步长,每次累加的数据的间隔

1
2
3
4
val d = blas.dasum(2,Array(1.0,2.0,3.0,4.0,5.0),2,2)

println(d)
//8.0

以步长2从索引2开始取2个数据,也即(3.0,5.0),执行加法,结果即8.0。

scopy

实现前一个数组复制到后一个数组中

1
2
3
4
5
6
public void scopy(int n, float[] sx, int _sx_offset, int incx, float[] sy, int _sy_offset, int incy);
参数解析:
n :需要复制的数据总数
sx|sy :sx为源数据,复制到sy
_sx_offset|_sy_offset :sx|sy数组数据复制|插入的起始的位置
incx|incy :步长,取数据的间隔

1
2
3
4
5
6
7
8
9
val a = Array(1.0f,2.0f,3.0f,4.0f,5.0f)
val b = Array(1.0f,2.0f,3.0f,4.0f,5.0f)

blas.scopy(2,a,0,1,b,2,2)

println(a.mkString(","))
println(b.mkString(","))
//1.0,2.0,3.0,4.0,5.0
//1.0,2.0,1.0,4.0,2.0

从索引0,以步长1从a取2个数字,也即(1.0,2.0),放到b的从索引2开始,步长2的位置,原数据是(3.0,5.0),替换原数据,即得结果。

sdot

矢量的点积
$x = (a_1,a_2,…,a_n)$,$y = (b_1,b_2,…,b_n)$
$sdot(x,y) = a_1 \ast b_1+a_2 \ast b_2+\cdots+a_n \ast b_n$

1
2
3
4
5
6
public float sdot(int n, float[] sx, int _sx_offset, int incx, float[] sy, int _sy_offset, int incy);
参数解析:
n :需要计算的数据总数
sx|sy :sx|sy为数据源
_sx_offset|_sy_offset :sx|sy数组开始计算的位置
incx|incy :步长,取数据的间隔

1
2
3
4
5
6
7
val a = Array(1.0f,2.0f,3.0f,4.0f,5.0f)
val b = Array(1.0f,2.0f,3.0f,4.0f,5.0f)

val c = blas.sdot(2,a,2,1,b,1,1)

println(c)
//18.0

从索引2,以步长1从a取2个数字,也即(3.0,4.0),从索引1,以步长1从b取2个数字,也即(2.0,3.0),结果=3.0$\ast$2.0+4.0$\ast$3.0=18.0

sdsdot

同sdot,不过多加一个额外的系数
$x = (a_1,a_2,…,a_n)$,$y = (b_1,b_2,…,b_n)$
$sdot(x,y) = a_1 \ast b_1+a_2 \ast b_2+\cdots+a_n \ast b_n+c$
其中c为系数

1
2
3
4
5
6
7
public float sdsdot(int n, float sb, float[] sx, int _sx_offset, int incx, float[] sy, int _sy_offset, int incy);
参数解析:
n :需要计算的数据总数
sb :额外需要加的系数
sx|sy :sx|sy为数据源
_sx_offset|_sy_offset :sx|sy数组开始计算的位置
incx|incy :步长,取数据的间隔

1
2
3
4
5
6
val a = Array(1.0f,2.0f,3.0f,4.0f,5.0f)
val b = Array(1.0f,2.0f,3.0f,4.0f,5.0f)
val c = blas.sdsdot(2,11.0f,a,1,2,b,3,1)

println(c)
//39.0

从索引1,以步长2从a取2个数字,也即(2.0,4.0),从索引3,以步长1从b取2个数字,也即(4.0,5.0),结果=2.0$\ast$4.0+4.0$\ast$5.0+11.0=39.0

snrm2

欧几里得范数
$x = (a_1,a_2,…,a_n)$
$snrm2(x) = \sqrt{a_1^2+a_2^2+\cdots+a_n^2}$
其中c为系数

1
2
3
4
5
6
public float snrm2(int n, float[] x, int _x_offset, int incx);
参数解析:
n :需要计算的数据总数
x :x为数据源
_x_offset :x数组开始计算的位置
incx :步长,取数据的间隔

1
2
3
4
5
val a = Array(1.0f,2.0f,3.0f,4.0f,5.0f)
val c = blas.snrm2(2,a,2,1)

println(c)
//5.0

从索引2,以步长1从a取2个数字,也即(3.0,4.0),勾三股四弦五结果=5.0

isamax

求最大值的位置

1
2
3
4
5
6
public int isamax(int n, float[] sx, int _sx_offset, int incx);
参数解析:
n :需要计算的数据总数
sx :sx为数据源
_x_offset :sx数组开始计算的位置
incx :步长,取数据的间隔

1
2
3
4
5
val a = Array(1.0f,2.0f,3.0f,4.0f,5.0f)
val c = blas.isamax(4,a,0,1)

println(c)
//4

从索引0开始以步长1取出4个数,也即(1.0f,2.0f,3.0f,4.0f),计算最大值的位置(从1开始)

热评文章