Matrix Arrayfire 2017
Matrix Arrayfire 2017
on GPU
with ArrayFire - Python
and ArrayFire - C/C++
Andrzej Chrzȩszczyk
Kielce 2017
Foreword
• All code samples in this version were checked with ArrayFire 3.5. Most
of examples from our previous text with similar title: [Link]
com/wp-content/uploads/2014/03/arrayfire matrix computations
chrzeszczyk [Link] do not work in ArrayFire 3.5, due to changes
in the syntax, so extensive changes in our older text were necessary.
Foreword . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1 ArrayFire-Python 5
1.1 Introductory remarks . . . . . . . . . . . . . . . . . . . . . 5
1.2 Defining arrays . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3 Random arrays . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.4 Rearranging arrays . . . . . . . . . . . . . . . . . . . . . . . 16
1.5 Matrix addition multiplication and powering . . . . . . . . . 20
1.6 Sums and products of elements . . . . . . . . . . . . . . . . 21
1.7 Mean, variance, standard deviation and histograms . . . . . 23
1.8 Solving linear systems . . . . . . . . . . . . . . . . . . . . . 27
1.9 Matrix inverse . . . . . . . . . . . . . . . . . . . . . . . . . . 30
1.10 LU decomposition . . . . . . . . . . . . . . . . . . . . . . . . 31
1.11 Cholesky decomposition . . . . . . . . . . . . . . . . . . . . 34
1.12 QR decomposition . . . . . . . . . . . . . . . . . . . . . . . 37
1.13 Singular Value Decomposition . . . . . . . . . . . . . . . . . 39
1.14 Plotting with ArrayFire . . . . . . . . . . . . . . . . . . . . 42
1.14.1 Two-dimensional plot . . . . . . . . . . . . . . . . . . 42
1.14.2 Surface plot . . . . . . . . . . . . . . . . . . . . . . . 43
1.14.3 Image-plot . . . . . . . . . . . . . . . . . . . . . . . . 43
2 ArrayFire-C/C++ 45
2.1 Introductory remarks . . . . . . . . . . . . . . . . . . . . . . 45
2.2 Defining arrays . . . . . . . . . . . . . . . . . . . . . . . . . 46
2.3 Random arrays . . . . . . . . . . . . . . . . . . . . . . . . . 51
2.4 Rearranging arrays . . . . . . . . . . . . . . . . . . . . . . . 53
2.5 Determinant, norm and rank . . . . . . . . . . . . . . . . . . 58
2.6 Elementary arithmetic operations on matrices . . . . . . . . 60
2.7 Sums and products of elements . . . . . . . . . . . . . . . . 64
2.8 Dot product . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
CONTENTS 4
ArrayFire-Python
In the present chapter we assume that the user has an access to a system
with ArrayFire and Python installed.
This approach allows for executing our examples step by step and for ob-
serving the obtained results. Of course, it is also possible to create python
scripts and execute all the commands at once. Note that in the interactive
session the print commands can be omitted. We use these commands to
allow for both options.
................
PACKAGE CONTENTS
algorithm
arith
array
1.1 Introductory remarks 6
base
bcast
blas
cuda
data
device
features
graphics
image
index
interop
lapack
library
opencl
random
signal
sparse
statistics
tests (package)
timer
util
vision
..............
l=dir(af)
for i in range(20,len(l)/2): print ’%-30s %-30s’ % (l[2*i],l[2*i+1])
As the result one obtains four pages of output, but we show only a few lines.
... ...
abs accum
acos acosh
algorithm all_true
alloc_device alloc_host
alloc_pinned any_true
array asin
... ...
... ...
1.1 Introductory remarks 7
lower lu
lu_inplace match_template
math matmul
maxfilt maxof
mean mean_shift
... ...
... ...
qr qr_inplace
randn random
randu range
rank read_array
... ...
... ...
solve solve_lu
sort sort_by_key
sort_index sparse
stdev sum
susan svd
svd_inplace sync
... ...
... ...
To any element of the obtained list the help function can be used. This
gives us the access to the information concerning the chosen function. For
example:
help([Link])
Parameters
----------
A: [Link]
A 2 dimensional arrayfire array representing the coef-
ficients of the system.
B: [Link]
1.2 Defining arrays 8
Returns
-------
X: [Link]
A 1 or 2 dimensional arrayfire array representing the
unknowns in the system.
ArrayFire Python interface allows for easy definitions of vectors and matri-
ces. The command help([Link]) shows that we have all necessary types
at our disposal. We can compare them with corresponding Numpy types.
ArrayFire Numpy
[Link].f32 np.float32 for float
[Link].f64 np.float64 for double
[Link].b8 [Link] for bool
[Link].u8 np.uint8 for unsigned char
[Link].s16 np.int16 for signed 16 bit integer
[Link].u16 np.uint16 for unsigned 16 bit integer
[Link].s32 np.int32 for signed 32 bit integer
[Link].u32 np.uint32 for unsigned 32 bit integer
[Link].s64 np.int64 for signed 64 bit integer
[Link].u64 np.uint64 for unsigned 64 bit integer
[Link].c32 np.complex64 for 32 bit complex number
[Link].c64 np.complex128 for 64 bit complex number
A new ArrayFire arrays can be defined using python array module or lists,
see help([Link]). The default type in ArrayFire is float32, while in
Numpy the default is float64.
1.2 Defining arrays 9
To obtain new ArrayFire arrays one can also use Numpy arrays and the
[Link] function.
import numpy as np
x=[Link]([[0,1,2],[3,4,5],[6,7,8]],’float32’)
print(x)
[[ 0., 1., 2.],
[ 3., 4., 5.],
[ 6., 7., 8.]] # Numpy uses row major format
import arrayfire as af
y=[Link]([Link],[Link],[Link])
[Link](y,0)
0 3 6 # [Link]() uses
1 4 7 # column major format
2 5 8
# the same, shorter way
y=[Link](range(9),(3,3))
[Link](y,0)
0 3 6 # [Link]() uses
1 4 7 # column major format
2 5 8
z=af.np_to_af_array(x);
1.2 Defining arrays 10
[Link](z,0)
0 1 2 # row major version
3 4 5
6 7 8
The constant function from ArrayFire one can replace in Numpy by ones
multiplied by a constant, or by the pair empty and fill.
##### Arrayfire ##### ##### Numpy #####
[Link](a,0) a=[Link]((3,3),’float32’)
2 2 2 [Link](2)
2 2 2 print(a)
2 2 2 [[ 2. 2. 2.]
[ 2. 2. 2.]
[ 2. 2. 2.]
import numpy as np
import arrayfire as af
a=[Link](lambda i,j:i+j,(3,3),dtype=’float32’)
A=af.np_to_af_array(a)
[Link](A,0)
0 1 2 # a[i,j]=i+j
1 2 3
2 3 4
There are also [Link], [Link] functions, which define diagonal matri-
ces.
import arrayfire as af
a=[Link]([Link](4)+3,num=0,extract=False) # Diagonal matrix
[Link](a,0) # in ArrayFire
3 0 0 0
0 4 0 0
0 0 5 0
0 0 0 6
import numpy as np
[Link]([Link](4)+3).astype(’float32’) # Diagonal matrix
print(a) # in Numpy
[[ 3. 0. 0. 0.]
[ 0. 4. 0. 0.]
[ 0. 0. 5. 0.]
[ 0. 0. 0. 6.]]
1.3 Random arrays 13
Very often random arrays from uniform or normal distributions are used.
##### ArrayFire ##### ##### Numpy #####
rn=[Link](N,N) # Warm up
t0=time();rn=[Link](N,N);[Link](rn);[Link]();t=time()-t0
print "time: %.4f sec." % t
time: 0.0014 sec. # normal matr. generation time
Similar computations in Numpy take much more time but the single and
double precision versions have similar performance. Below we present uni-
form, single precision versions in three ways.
import numpy as np
from time import time
import numpy.random_intel as rndi
N=8000
# uniform, random matrix in random_intel, single precision (1)
t0=time();ru=[Link](N*N);t=time()-t0
print t
0.168909788132
# Transposition of A
AT=A.T aT=a.T
[Link](AT,0) print(aT)
1.4 Rearranging arrays 17
0 1 2 [[ 0. 3. 6.]
3 4 5 [ 1. 4. 7.]
6 7 8 [ 2. 5. 8.]]
# Conjugation
BC=[Link](B) bc=[Link](b) # bc=[Link]( )
[Link](BC,0) print(bc)
(0,-0) (1,-2) (2,-4) [[ 0. -0.j 1. -2.j 2. -4.j]
(3,-6) (4,-8) (5,-10) [ 3. -6.j 4. -8.j 5.-10.j]
(6,-12) (7,-14) (8,-16) [ 6.-12.j 7.-14.j 8.-16.j]]
# Conjugate transposition
BH=B.H bh=[Link]().T
[Link](BH,0) print(bh)
(0,-0) (3,-6) (6,-12) [[ 0. -0.j 3. -6.j 6.-12.j]
(1,-2) (4,-8) (7,-14) [ 1. -2.j 4. -8.j 7.-14.j]
(2,-4) (5,-10) (8,-16) [ 2. -4.j 5.-10.j 8.-16.j]]
A=[Link]([Link](9),3,3) a=[Link](9.0).reshape(3,3)
[Link](A,0) # column-wise print(a) # row-wise
0 3 6 [[ 0. 1. 2.]
1 4 7 [ 3. 4. 5.]
2 5 8 [ 6. 7. 8.]]
1.4 Rearranging arrays 18
# Flip horizontally
AFH=[Link](A) afh=[Link](a)
[Link](AFH,0) print(afh)
2 5 8 [[ 6. 7. 8.]
1 4 7 [ 3. 4. 5.]
0 3 6 [ 0. 1. 2.]]
# Flip vertically
AFV=[Link](A,1) afv=[Link](a)
[Link](AFV,0) print(afv)
6 3 0 [[ 2. 1. 0.]
7 4 1 [ 5. 4. 3.]
8 5 2 [ 8. 7. 6.]]
The array can be flattened and upper or lower triangular parts can be
extracted.
##### ArrayFire ##### ##### Numpy #####
A=[Link]([Link](9),3,3) a=[Link](9.0).reshape(3,3)
[Link](A,0) # column-wise print(a) # row-wise
0 3 6 [[ 0. 1. 2.]
1 4 7 [ 3. 4. 5.]
2 5 8 [ 6. 7. 8.]]
# Flattened A
AF=[Link](A) af=[Link]()
[Link](AF,0) print(af)
0 [ 0. 1. 2. 3. 4. 5. 6. 7. 8.]
1
2
3
4
5
6
7
8
0 0 0 [[ 0. 0. 0.]
1 4 0 [ 3. 4. 0.]
2 5 8 [ 6. 7. 8.]]
AF=[Link](9) a=[Link](9.0)
[Link](AF,0) print(a)
0 [0. 1. 2. 3. 4. 5. 6. 7. 8.]
1
2
3
4
5
6
7
8
# Shift
S=[Link](AF,1) s=[Link](a,1)
[Link](S,0) print(s)
8 [8. 0. 1. 2. 3. 4. 5. 6. 7.]
0
1
2
3
4
5
6
7
1.5 Matrix addition multiplication and powering 20
To obtain the sum, the difference and the product of two matrices one can
use the operations +, – and matmul.
##### ArrayFire ##### ##### Numpy #####
import [Link] as la
t0=time();b=[Link](1.0,a,a);t=time()-t0
print t
2.72001791 # CPU i7-6700, Scipy with MKL
elements.
Let us begin with simple examples.
##### ArrayFire ##### ##### Numpy #####
# 2*a # 2*a
a=a*2 a=a*2
[Link](a,0) print(a)
2 2 2 [[ 2. 2. 2.]
2 2 2 [ 2. 2. 2.]
2 2 2 [ 2. 2. 2.]
# pi^2/6 in numpy
print([Link]**2/6)
1.64493406685
# Variance with the help of mean # Variance with the help of mean
print(v) print(v)
1.7 Mean, variance, standard deviation and histograms 25
0.0833306899185 0.0833273097913
# Theoretical variance: # Theoretical variance:
# 1/12=0.08333333... # 1/12=0.08333333...
N=10000000;x=[Link](N) N=10000000;x=[Link](N)
m=[Link](x);v=[Link](x*x)-m*m m=[Link](x);v=[Link](x*x)-m*m
print(m) print(m)
-0.000223015042138 0.00023204327067
# Theoretical mean: 0 # Theoretical mean: 0
# Variance with the help of mean # Variance with the help of mean
print(v) print(v)
0.999524424408 0.999263876548
# Theoretical variance: 1 # Theoretical variance: 1
B=[Link](A,b) B=[Link](a,b)
# Solution X should be equal to b # Sol. X should be equal to b
X=[Link](A,B) x=[Link](a,B)
[Link](X)
0.1570 [[ 0.32301685]
0.3725 [ 0.30273178]
0.9624 [ 0.83872396]]
# Max error # Max error
print([Link]([Link](X-b))) print([Link]([Link](x-b))
2.83122062683e-07 3.57628e-07
print(A.is_single()) print([Link])
True float32
On GTX 1080 card we solved an 8000x8000 system using single and double
precision.
import arrayfire as af
from time import time
N=8000;A=[Link](N,N) # 8000x8000 single precision
b=[Link](N,1);B=[Link](A,b)
t0=time();X=[Link](A,B);[Link](X);[Link]();t=time()-t0
print "time:",t
time: 0.194478988647 # Solving time in single prec.
# on GTX 1080
print([Link]([Link](X-b))) # Max error
0.00577610623837
# continuation
N=8000 # 8000x8000 double precision
A=[Link](N,N)
b=[Link](N)
B=[Link](A,b)
X=[Link](A,B)
t0=time();X=[Link](A,B);t=time()-t0
print "time:",t # Solving time in double prec.
time: 2.51149082184 # using MKL on i7-6700
print "time:",t
time: 2.46150708199
N=8000
A=[Link](N,N)
b=[Link](N)
B=[Link](A,B)
lu,piv,info=[Link](A)
x,info=[Link](lu,piv,B) # dgetrf+dgetrs
[Link]([Link](A,x),B)
True
t0=time();lu,piv,info=[Link](A); # new line for editing
x,info=[Link](lu,piv,B);t=time()-t0 # purposes
print "time:",t
time: 2.446714077
A · A−1 = I,
where I denotes the identity matrix. Below we show the functions inverse
and inv in action.
##### ArrayFire ##### ##### Numpy/Scipy #####
AIA=[Link](A,IA) # A*A^-1
I=[Link](N,N) # I - identity
print [Link]([Link](AIA-I)) # A*A^-1 -I
0.00331087806262 # Max error
import numpy as np
from time import time
import [Link] as la
import numpy.random_intel as rndi
a=[Link](N,N).astype(’float32’) # 8000x8000 [Link]
t0=time();ia=[Link](a);t=time()-t0
print(t) # Inversion time in Scipy
3.45366692543 # on i7-6700 CPU
I=[Link](N,’f’) # I - identity
aia=[Link](a,ia) # A*A^-1
print [Link]([Link](aia-I)) # A*A^-1 -I
0.00164378 # Max error
1.10 LU decomposition
A = P LU,
# LU = L*U # lu=l*u
LU=[Link](L,U) lu=[Link](l,u)
# P*A - LU plu=[Link](p,lu)
print([Link]([Link](A[P]-LU))) # p*l*u=a
0.0 [Link](plu,a)
True
To check the ArrayFire efficiency in LU factorization one can use for example
the following script.
import arrayfire as af
from time import time
N=8000
A=[Link](N,N) # 8000x8000 random matrix
t0=time();LU=[Link](A);[Link](LU[0]);[Link]();t=time()-t0
print(t)
0.223619937897 # LU decomp time on GTX 1080
N=8000
a=[Link](N,N).astype(’f’)
t0=time();lup=la.lu_factor(a);t=time()-t0
print(t)
1.4020049572
# direct use of lapack sgetrf
t0=time();lupi=[Link](a);t=time()-t0
print(t) # time for single precision
1.35158014297 # sgetrf on i7-6700 CPU
A = L · LT or A = LT · L,
where L is a lower triangular matrix in the first formula and upper triangular
in the second one.
In ArrayFire one can use cholesky function, which gives a factorization in
the form A = RT · R with upper triangular matrix R (if the lower triangular
version is preferred then the command [Link](A,is upper=False)
1.11 Cholesky decomposition 35
should be used). In Scipy we also use the version with upper triangu-
lar matrix R. If the lower triangular version is preferred, the command
cholesky(A,lower=True or cholesky(A,lower) should be used.
# continuation # continuation
A1=[Link]() A1=[Link]()
af.cholesky_inplace(A1) B,lower=la.cho_factor(A1)
R=[Link](A1) R=[Link](B)
[Link](R) print(R)
0.6423 0.6862 0.7595 [[ 1.2674 1.3576 1.0975]
0.0000 0.2282 -0.7275 [ 0. 0.2956 -0.3014]
0.0000 0.0000 0.2529 [ 0. 0. 0.6136]]
R1=[Link](R.T,R) print([Link](A,[Link](R.T,R)))
print([Link]([Link](R1-A))) True
0.0
1.11 Cholesky decomposition 36
In Scipy one can also use lapack functions dpotrf, spotrf directly.
import numpy as np
from [Link] import rand
import [Link] as la
A=rand(3,3)
A=[Link](A.T,A)
R,info=[Link](A)
# check if A=R^T*R
print([Link](A,[Link](R.T,R)))
True
t0=time();res=[Link](A);t=time()-t0
print(t) # direct use of lapack dpotrf
2.36906909943
t0=time();res=[Link](A);t=time()-t0
print(t) # direct use lapack spotrf
1.14207792282
print([Link])
float32
1.12 QR decomposition
A = Q · R,
A1=[Link]() A1=[Link]()
# in place QR (if Q is not essential)
T=af.qr_inplace(A1) R1=[Link](A,mode=’r’)
# compare with the previous # compare with the previous
R1=[Link](A1) print([Link](R,R1))
Q,R,T=[Link](A) True
[Link]([Link](R1-R))
0.0
In Scipy we have an additional versions of QR decomposition with pivoting.
# continuation
Q,R,P=[Link](A,pivoting=True) # QR with pivoting: A*P=Q*R
[Link](A[:,P],[Link](Q,R)) # chck if P*A=Q*R
True
1.13 Singular Value Decomposition 39
# check if U is orthogonal
[Link]([Link](U.T,U),0) print([Link]([Link](U,U.T)))
1 -0 0 0 [[ 1., -0., 0., 0.],
-0 1 -0 0 [-0., 1., 0., 0.],
0 -0 1 0 [ 0., 0., 1., 0.],
0 0 0 1 [ 0., 0., 0., 1.]]
# check if Vt is orthogonal
[Link]([Link](Vt,Vt.T),0) print([Link]([Link](Vt,Vt.T)))
1 0 0 [[ 1., -0., 0.],
0 1 -0 [-0., 1., -0.],
0 -0 1 [ 0., -0., 1.]]
1.13 Singular Value Decomposition 41
There are also inplace versions of SVD, where the matrix A may be over-
written.
##### ArrayFire ##### ##### Numpy/Scipy #####
1.44229793549
# direct use of lapack
lwork=\
[Link]._compute_lwork([Link].sgesdd_lwork,[Link][0],[Link][1])
t0=time();U,S,Vt,info=[Link](A);t=time()-t0
print(t)
1.323127985 # Scipy lapack SVD decomp.
import arrayfire as af
import math
N2 = 60
N = 2 * N2
# grid x,y=2pi*(-1,-1+2/N,-1+4/N,...,1-2/N,1)
x =2*[Link]*([Link](d0=N,d1=1,tile_dims=(1,N))-N2)/N2
y =2*[Link]*([Link](d0=1,d1=N,tile_dims=(N,1))-N2)/N2
win = [Link](1200, 600, "3D Surface")
while not [Link]():
z=[Link]([Link](x*x+y*y))
[Link](x, y, z) # Three-dimensional plot
1.14.3 Image-plot
There is also image function in ArrayFire. Let us show how to plot the
function from the previous example using image.
import arrayfire as af
import math
N2 = 120
1.14 Plotting with ArrayFire 44
N = 2 * N2
# grid x,y=2pi*(-1,-1+2/N,-1+4/N,...,1-2/N,1)
x =2*[Link]*([Link](d0=N+1,d1=1,tile_dims=(1,N+1))-N2)/N2
y =2*[Link]*([Link](d0=1,d1=N+1,tile_dims=(N+1,1))-N2)/N2
win = [Link](600, 600, "image")
win.set_colormap([Link])
while not [Link]():
z=[Link]([Link](x*x+y*y))
[Link](z) # image plot
ArrayFire-C/C++
Let us begin with a short program showing some details concerning the
ArrayFire version and hardware installed.
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(){
info(); // Information on installed
return 0; // software and hardware
}
2.2 Defining arrays 46
/*
ArrayFire v3.5.0 (CUDA, 64-bit Linux, build 05999f3)
Platform: CUDA Toolkit 8, Driver: 378.13
[0] GeForce GTX 1080, 8111 MB, CUDA Compute 6.1
*/
The pages
• [Link]/docs/using on [Link],
• [Link]/docs/using on [Link],
• [Link]/docs/using on [Link]
in1
[3 3 1 1]
1.0000 1.0000 1.0000
1.0000 1.0000 1.0000
1.0000 1.0000 1.0000
in2
[3 3 1 1]
1.0000 0.0000 0.0000
0.0000 1.0000 0.0000
0.0000 0.0000 1.0000
constant(1,3,3,c32)
[3 3 1 1]
(1.0,0.0) (1.0,0.0) (1.0,0.0)
(1.0,0.0) (1.0,0.0) (1.0,0.0)
(1.0,0.0) (1.0,0.0) (1.0,0.0)
constant(1,3,3,u32)
[3 3 1 1]
1 1 1
1 1 1
1 1 1
*/
The default type is f32 i.e. float. The basic types have the abbreviations:
• f32 –real single-precision (float)
Very often an array is defined on the host. Sometimes we want to create its
copy on the device. In this case we can use the cudaMalloc and cudaMemcpy
functions.
#include <stdio.h>
#include <arrayfire.h>
#include <af/cuda.h>
using namespace af;
int main(void){
float host_ptr[] = {0,1,2,3,4,5,6,7,8};
array a(3, 3, host_ptr); // 3x3 f32 matrix from host pointer
af_print(a);
float *device_ptr;
cudaMalloc((void**)&device_ptr, 9*sizeof(float));
cudaMemcpy(device_ptr, host_ptr, 9*sizeof(float),
cudaMemcpyHostToDevice);
array b( 3,3,device_ptr, afDevice); // 3x3 f32 matrix
af_print(b); // from device pointer
return 0;
}
/*
a
[3 3 1 1] // from host
0.0 3.0 6.0
1.0 4.0 7.0
2.0 5.0 8.0
b // from device
[3 3 1 1]
0.0 3.0 6.0
1.0 4.0 7.0
2.0 5.0 8.0
*/
2.2 Defining arrays 49
The last array can be also defined on the device using the sequence operation
seq and the moddims command.
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(void){
array in=seq(0,8);
af_print(in,1);
array b=moddims(in,3,3); // b as 3x3 matrix
af_print(b,1);
return 0;
}
/*
in // in as a column
0.0
1.0
2.0
3.0
4.0
5.0
6.0
7.0
8.0
b // b as a 3x3 matrix
0.0 3.0 6.0
1.0 4.0 7.0
2.0 5.0 8.0
*/
If the array elements are given by a formula we can use the following mod-
ification.
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(void){
float *host_ptr=new float[3*3];
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
host_ptr[3*i+j]=i-j;
2.2 Defining arrays 50
dB
[3 1 1 1]
(0.0,1.0)
(2.0,3.0)
(4.0,5.0)
*/
2.3 Random arrays 51
ArrayFire allows for an easy and efficient generation of random arrays from
uniform and normal distributions.
Let us begin with small matrices.
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(void){
int n = 3;
array A = randu(n, n); // uniformly distributed
printf("uniform:\n"); // f32 3x3 random matrix
af_print(A);
A = randn(n, n, c32);
printf("normal,complex:\n"); // normally distributed
af_print(A); // compl. (c32) 3x3 matr.
return 0;
}
/*
uniform:
A
[3 3 1 1]
0.7402 0.9690 0.6673
0.9210 0.9251 0.1099
0.0390 0.4464 0.4702
normal,complex:
A
[3 3 1 1]
(-0.1370,-1.2768) (-1.0515,-0.5493) (1.1401,0.7409)
(1.0782,-0.6237) (2.0714,1.2586) (-0.0625,0.5143)
(1.5255,0.6660) (1.4206,0.3912) (0.5286,-1.0861)
*/
Using floor, ceil or round functions one can obtain random matrices
with integer entries.
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(void){
2.3 Random arrays 52
floor(A)
[3 3 1 1]
-5 8 5
-4 2 1
-1 5 1
ceil(A)
[3 3 1 1]
-4 9 6
-3 3 2
-0 6 2
round(A)
[3 3 1 1]
-5 8 6
-3 2 1
-1 5 1
*/
Using 8000x8000 random matrices we can check the efficiency of GPU ran-
dom generators.
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(void){
int n = 8e3;
2.4 Rearranging arrays 53
return 0;
}
/*
A
[3 3 1 1]
0.7402 0.9690 0.6673
0.9210 0.9251 0.1099
0.0390 0.4464 0.4702
A.T()
[3 3 1 1]
0.7402 0.9210 0.0390
0.9690 0.9251 0.4464
0.6673 0.1099 0.4702
A
[3 3 1 1]
(0.5170,0.0714) (0.6734,0.1202) (0.1583,0.7056)
(0.3335,0.4896) (0.1631,0.5666) (0.9808,0.1623)
(0.1845,0.5206) (0.2072,0.4028) (0.4279,0.7804)
conjg(A)
[3 3 1 1]
(0.5170,-0.0714) (0.6734,-0.1202) (0.1583,-0.7056)
(0.3335,-0.4896) (0.1631,-0.5666) (0.9808,-0.1623)
(0.1845,-0.5206) (0.2072,-0.4028) (0.4279,-0.7804)
A.H()
[3 3 1 1]
(0.5170,-0.0714) (0.3335,-0.4896) (0.1845,-0.5206)
(0.6734,-0.1202) (0.1631,-0.5666) (0.2072,-0.4028)
(0.1583,-0.7056) (0.9808,-0.1623) (0.4279,-0.7804)
*/
One can flip the array horizontally or vertically.
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(){
array A=randu(3, 3);
2.4 Rearranging arrays 55
af_print(A);
af_print(flip(A,0)); // Flip horizontally
af_print(flip(A,1)); // Flip vertically
return 0;
}
/*
A
[3 3 1 1]
0.7402 0.9690 0.6673
0.9210 0.9251 0.1099
0.0390 0.4464 0.4702
flip(A,0)
[3 3 1 1]
0.0390 0.4464 0.4702
0.9210 0.9251 0.1099
0.7402 0.9690 0.6673
flip(A,1)
[3 3 1 1]
0.6673 0.9690 0.7402
0.1099 0.9251 0.9210
0.4702 0.4464 0.0390
*/
The array can be also flattened and upper and lower triangular part can be
extracted.
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(){
array A=randu(3, 3);
af_print(A);
af_print(flat(A)); // Flattened A
af_print(lower(A)); // Lower triang. part
af_print(upper(A)); // Upper triang. part
return 0;
}
/*
A
[3 3 1 1]
2.4 Rearranging arrays 56
flat(A)
[9 1 1 1]
0.7402
0.9210
0.0390
0.9690
0.9251
0.4464
0.6673
0.1099
0.4702
lower(A)
[3 3 1 1]
0.7402 0.0000 0.0000
0.9210 0.9251 0.0000
0.0390 0.4464 0.4702
upper(A)
[3 3 1 1]
0.7402 0.9690 0.6673
0.0000 0.9251 0.1099
0.0000 0.0000 0.4702
*/
There are also shift and rotate operations.
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(){
array A=seq(0,8); // A: 0,1,...,8
af_print(A.T(),1);
af_print(shift(A,1).T(),1); // Shift operation
af_print(rotate(A,3).T(),1); // Rotate operation
return 0;
}
2.4 Rearranging arrays 57
/*
A.T()
0 1 2 3 4 5 6 7 8
shift(A,1).T()
8 0 1 2 3 4 5 6 7
rotate(A,3).T()
0 7 6 5 4 3 2 1 0
*/
It is possible to join two matrices into one larger matrix.
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(){
array A1=seq(1,3); // A1: [1,2,3]^T
array A2=seq(4,6); // A2: [4,5,6]^T
af_print(join(0,A1,A2),0); // Join vertically
af_print(join(1,A1,A2),0); // Join horizontally
A1=constant(1,3,3); // A1: 3x3 matrix of ones
A2=constant(0,3,3); // A2: 3x3 matrix of zeros
af_print(join(0,A1,A2),0); // Join vertically
af_print(join(1,A1,A2),0); // Join horizontally
return 0;
}
/*
join(0,A1,A2) // Join vertically
1
2
3
4
5
6
In the following example we check the det function in the case of the upper
triangular 5000x5000 matrix.
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(){
int n = 5e3;
array A=upper(constant(1,n,n,f32)); // A: 5000x5000 upper
// Triang. matrix of ones
float d = det<float>(A);
2.5 Determinant, norm and rank 59
timer::start();
d = det<float>(A); // Determinant of A
af::sync();
printf("det time: %g\n", timer::stop());
printf("%f\n",d);
return 0;
}
//det time: 0.06443
//1.000000 // Value of det
In the norm function we can use an additional parameter to specify what
kind of norm we have in mind:
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(){
array A=seq(1,4);
af_print(A);
printf("%lf\n",norm(A)); //euclid
printf("%g\n",norm(A,AF_NORM_EUCLID)); //euclid
printf("%g\n",norm(A,AF_NORM_VECTOR_1)); //L_1
printf("%g\n",norm(A,AF_NORM_VECTOR_2)); //L_2
printf("%g\n",norm(A,AF_NORM_VECTOR_P,4)); //L_4
printf("%g\n",norm(A,AF_NORM_VECTOR_INF)); //L_inf
return 0;
}
/*
A
[4 1 1 1]
1.0000
2.0000
3.0000
4.0000
In ArrayFire one can also find the function rank which computes the number
of linearly independent rows or columns of an array.
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(){
array A=randu(3,4); // A: random 3x4 matrix
unsigned r=rank(A); // Rank of A
printf("%d\n",r);
return 0;
}
//3 // Rank of A
In some single precision calculations the default tolerance 1e-5 (which means
that only the singular values greater than this number are considered)
should be changed.
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(){
array A=randu(600,400); // A: 600x400 random matrix
A(seq(300),span)=0; // First 300 rows set to 0
unsigned r=rank(A,1e-3); // Rank of A, tolerance 1e-3
printf("%d\n",r);
return 0;
}
//300 // Rank of A
In ArrayFire the sum, difference and the product of two matrices one can
obtain using the +, – and matmul operators.
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(){
array A=moddims(seq(0,8),3,3);
array B=array(seq(1,9),3,3);
2.6 Elementary arithmetic operations on matrices 61
af_print(A,0);
af_print(B,0);
af_print(A+B,0);
af_print(A-B,0);
af_print(matmul(A,B),0);
return 0;
}
/*
A
[3 3 1 1]
0 3 6
1 4 7
2 5 8
B
[3 3 1 1]
1 4 7
2 5 8
3 6 9
A+B
[3 3 1 1]
1 7 13
3 9 15
5 11 17
A-B
[3 3 1 1]
-1 -1 -1
-1 -1 -1
-1 -1 -1
matmul(A,B)
[3 3 1 1]
24 51 78
30 66 102
36 81 126
*/
The * operator gives the element-wise product.
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(){
2.6 Elementary arithmetic operations on matrices 62
A*A
[3 3 1 1]
2.6 Elementary arithmetic operations on matrices 63
0 9 36
1 16 49
4 25 64
af::pow(A,2)
[3 3 1 1]
0 9 36
1 16 49
4 25 64
af::pow(A,4)
[3 3 1 1]
0 81 1296
1 256 2401
16 625 4096
*/
The inputs for pow function can be two arrays or an array and a scalar.
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(){
array A=array(seq(0,8),3,3);
af_print(A);
af_print(pow(A,2.0f),0); // scalar exponent
af_print(pow(A,constant(2,3,3)),0); // matrix exponent
return 0;
}
/*
A
[3 3 1 1]
0 3 6
1 4 7
2 5 8
Using the functions sum and prod we can sum or multiply all entries of an
array, or elements of some subsets, for example of rows or columns.
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(){
2.7 Sums and products of elements 65
array A=constant(2,3,3);
float su,pro;
af_print(A,0); // A
af_print(sum(A),0); // Sums of columns
af_print(sum(A,1),0); // Sums of rows
su = sum<float>(A); // Sum of all elem.
printf("sum= %f\n",su);
af_print(product(A),0); // Products of columns
af_print(product(A,1),0); // Products of rows
pro=product<float>(A); // Product of all elem.
printf("product= %f\n",pro);
return 0;
}
/*
A // A
[3 3 1 1]
2 2 2
2 2 2
2 2 2
af_print(inn,0);
return 0;
}
/*
A1 // A1
[3 1 1 1]
1
1
1
A2 // A2
[3 1 1 1]
0
1
2
dot product:
inn
[1 1 1 1]
3 // Inner product of A1,A2
*/
Now let us try to compute the dot product of A times A for 5000x1 matrix
of ones.
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(){
unsigned m = 5e3;
array A1 = constant(1,m,1); // 5000x1 matrix of ones
array inn = dot(A1,A1); // warm-up
timer::start();
inn = dot(A1,A1); // dot product A1 and A1
printf("dot prod. time: %g\n", timer::stop());
printf("dot prod. value: \n");
af_print(inn);
return 0;
}
/*
dot prod. time: 4.1e-05
2.9 Mean, variance and standard deviation 68
mean(A)
[1 3 1 1]
1.0000 4.0000 7.0000 // Averages of columns
mean(A,1)
[3 1 1 1]
3.0000
2.9 Mean, variance and standard deviation 69
var(A,0,1)
[3 1 1 1]
9.0000
9.0000 // Variances of rows
9.0000
stdev(A)
[1 3 1 1]
0.8165 0.8165 0.8165 // Std. dev. of columns
stdev(A,1)
[3 1 1 1]
2.4495
2.4495 // Std. dev. of rows
2.4495
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(void){
int n = 8e3;
float a,v;
array A = randn(n, n,f32); // A: 8000x8000 matrix
for(int k=0;k<3;k++){
timer::start();
2.9 Mean, variance and standard deviation 70
a=mean<float>(A); // Mean of A
v=var<float>(A); // Variance of A
array hi=histogram(A,100); // Histogram of A
af::sync();
printf(" time: %g\n", timer::stop());
printf(" mean(A): %g\n", a);
printf(" var(A): %g\n", v);
}
// af_print(hi); // Redirect to a file and use gnuplot
return 0; // or use the ArrayFire graphics (next code sample)
}
/*
time: 0.134017 // Time for mean, var, hist.
mean(A): -0.000159369 // Theoretical mean: 0
var(A): 0.999838 // Theoretical variance: 1
time: 0.008637
mean(A): -0.000325331
var(A): 1.00042
time: 0.00914
mean(A): -0.000325331
var(A): 1.00042
*/
The function inv gives the inverse matrix of A, i.e. a matrix A−1 such that
A · A−1 = I,
1 0 0
0 1 0
0 0 1
*/
To check the efficiency of ArrayFire inv function let us try to invert a
8000x8000 random matrix
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(void){
int n = 8e3;
array A = randu(n, n); // 8000x8000 random matrix
array I=identity(n,n);
array IA = inverse(A); // Warm up
for(int k=0;k<3;k++){
timer::start();
IA = inverse(A); // Inverse matrix
af::sync();
printf("inverting time: %g\n", timer::stop());
printf("%g\n",norm(matmul(A,IA)-I,AF_NORM_VECTOR_INF));
} //error
return 0;
}
/*
inverting time: 0.578662 // Inverting time
0.00256269 // L_inf norm of error (single prec.)
inverting time: 0.211771
0.00256269
inverting time: 0.21056
0.00256269
*/
2.12 LU decomposition
L // L
[3 3 1 1]
2.12 LU decomposition 75
U // U
[3 3 1 1]
0.9210 0.9251 0.1099
0.0000 0.4072 0.4656
0.0000 0.0000 0.3212
LU // L*U
[3 3 1 1]
0.9210 0.9251 0.1099
0.0390 0.4464 0.4702
0.7402 0.9690 0.6673
p // permutation of rows
[3 1 1 1]
1
2
0
A1 // copy of A
[3 3 1 1]
0.9210 0.9251 0.1099
0.0424 0.4072 0.4656
0.8037 0.5536 0.3212
matmul(l1,u1) // l1*l2
[3 3 1 1]
0.9210 0.9251 0.1099
0.0390 0.4464 0.4702
0.7402 0.9690 0.6673
[3 3 1 1]
0.9210 0.9251 0.1099
0.0390 0.4464 0.4702
0.7402 0.9690 0.6673
p1 // permutation of rows
[3 1 1 1]
1
2
0
*/
Let us consider a larger random matrix and check the efficiency of ArrayFire
lu function.
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(){
int n = 8e3;
array L, U, p;
array A = randu(n, n); // 8000x8000 matrix
array A1=[Link]();
for(int k=0;k<3;k++){
timer::start();
luInPlace(p,A1,false); // in place LU decomposition
af::sync();
printf("LU time: %g\n", timer::stop());
}
return 0;
}
LU time: 0.357864
LU time: 0.237496
LU time: 0.225606
array Uout;
array Lout; // unpacked version
cholesky(Uout,A,true); // cholesky decomp. - upper tr.
cholesky(Lout,A,false); // cholesky decomp. - lower tr.
af_print(Uout); // Uout is equal to Aupper
af_print(Lout); // Lout is equal to Alower
return 0;
}
/*
A // A
[3 3 1 1]
5.4804 1.8900 0.7063
1.8900 5.8503 0.5563
0.7063 0.5563 4.9404
2.14 QR decomposition
code.
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(){
int n = 2e3;
array Q, R;
array tau;
array A = randu(n, n); // Random 2000x2000 matrix
for(int k=0;k<3;k++){
timer::start();
qr(Q,R,tau,A); // QR decomposition
af::sync();
printf("qr time: %g\n", timer::stop());
}
return 0;
}
/*
qr time: 1.22483
qr time: 0.287861
qr time: 0.292128
*/
For m × n matrix A the singular value decomposition (SVD) has the form
A = U · S · V,
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(){
int m = 3, n = 3;
array U,S,Vt,s;
2.15 Singular Value Decomposition 82
U
[3 3 1 1]
-0.7120 0.3365 -0.6163
-0.6502 -0.6475 0.3975
-0.2653 0.6837 0.6798
Vt
[3 3 1 1]
2.15 Singular Value Decomposition 83
matmul(U,matmul(S,Vt)) // U*S*Vt = A
[3 3 1 1]
0.7402 0.9690 0.6673
0.9210 0.9251 0.1099
0.0390 0.4464 0.4702
matmul(U,U.T()) // U*U^T=I
[3 3 1 1]
1.0000 0.0000 -0.0000
0.0000 1.0000 0.0000
-0.0000 0.0000 1.0000
matmul(Vt,Vt.T()) // Vt*Vt^T=I
[3 3 1 1]
1.0000 -0.0000 0.0000
-0.0000 1.0000 0.0000
0.0000 0.0000 1.0000
*/
Now let us try to apply the svd function to larger matrix.
#include <stdio.h>
#include <arrayfire.h>
using namespace af;
int main(){
int n = 2e3;
array U,S,Vt,s; // U,V - orthogonal matrices
// S -matrix with [Link].
// s - vector of singul. val.
array A = randu(n, n); // 2000x2000 random matrix
timer::start();
svd(U,s,Vt, A); // SVD decomp. A=U*S*Vt
af::sync();
printf("SVD time: %g\n", timer::stop());
return 0;
}
/*
SVD time: 17.3208
2.16 Plotting with ArrayFire 84
2.16.3 Image-plot
There is also image function in ArrayFire which allows to plot the two
dimensional map of the function of two variables. Let us show how to plot
the function from the previous example using image.
#include <arrayfire.h>
#include <cstdio>
#include <math.h>
using namespace af;
static const int M = 100; // MxN xy grid
static const int N = M;
int main(int argc, char *argv[])
{
af::Window myWindow(800, 600, "Image");
const array x=3*af::Pi*iota(dim4(N,1),dim4(1,N))/M-1.5*af::Pi;
const array y=3*af::Pi*iota(dim4(1,N),dim4(N,1))/M-1.5*af::Pi;
array z = cos(sqrt((x*x)+(y*y)));
while(![Link]()) {
[Link](z); // image plot of
} // z=cos(sqrt(x^2+y^2))
return 0;
}