This post is first for me, nice to meet you!
In this post, I’d like to introduceMatft
library in swift to you.
Here is the source code link.
AboutMatft
Matft
is easy and fast to operate vector, matrix, tensor(N dimensional array), and even complex and image data!
The nameMatft
comes frommat-rix operation library in swi-ft.
If you are accustomed to usingNumpy
in python,Matft
will be the best option to achieve your matrix operation becauseMatft
's function names are similar toNumpy
's ones. (I'll show the examples later)
ManyNumpy
's functions has already implemented inMatft
.
You can operate complex number asNumpy
can, and there is conversion function that convertsCGImage
intoMfArray
(Matft
's object of n-dimensional array) vice versa.
Also, becauseMatft
uses theAccelerate
framework (link),Matft
keeps high performance (some functions even achieves higher thanNumpy
).
That's why I recommend to useMatft
when you'd like to operate vector, matrix, tensor(N dimensional array), and even complex and image data
Basic usage
MfType
Matft
's object to operate n-dimensional array is namedMfArray
inMatft
. AndMfArray
hasmftype
property topretend the n-dimensional array's type. (=Numpy
's dtype)
Pretending means that the stored data type will beFloat
orDouble
only even if you setMfType.Int
. So, if you input big number to MfArray, it may be cause to overflow or strange results in any calculation (+, -, *, /,... etc.). But I believe this is not problem in practical use.
MfType
supports many types such like;
publicenumMfType:Int{caseNone// UnsupporttedcaseBoolcaseUInt8caseUInt16caseUInt32caseUInt64caseUIntcaseInt8caseInt16caseInt32caseInt64caseIntcaseFloatcaseDoublecaseObject// Unsupported}
Initialization
In this section, I'll show the basic initialization ofMfArray
.
From Swift Array
You can initializeMfArray
from Swift's array. TheMfType
will be inferred properly.
leta=MfArray([[[-8,-7,-6,-5],[-4,-3,-2,-1]],[[0,1,2,3],[4,5,6,7]]])print(a)/*mfarray = [[[ -8, -7, -6, -5],[ -4, -3, -2, -1]],[[ 0, 1, 2, 3],[ 4, 5, 6, 7]]], type=Int, shape=[2, 2, 4]*/
Arrange
You can create the sequence array.
leta=Matft.arange(start:-8,to:8,by:1,shape:[2,2,4])print(a)/*mfarray = [[[ -8, -7, -6, -5],[ -4, -3, -2, -1]],[[ 0, 1, 2, 3],[ 4, 5, 6, 7]]], type=Int, shape=[2, 2, 4]*/
Repeated Numbers
You can create the repeated numbers array such likenumpy.ones() * N
.
leta=Matft.nums(Float(1),shape:[2,2,4])print(a)/*mfarray = [[[ 1.0, 1.0, 1.0, 1.0],[ 1.0, 1.0, 1.0, 1.0]],[[ 1.0, 1.0, 1.0, 1.0],[ 1.0, 1.0, 1.0, 1.0]]], type=Float, shape=[2, 2, 4]*/
Etc.
The other functions are below;
Matft | Numpy |
---|---|
*#Matft.shallowcopy | *numpy.copy |
*#Matft.deepcopy | copy.deepcopy |
Matft.nums | numpy.ones * N |
Matft.nums_like | numpy.ones_like * N |
Matft.arange | numpy.arange |
Matft.eye | numpy.eye |
Matft.diag | numpy.diag |
Matft.vstack | numpy.vstack |
Matft.hstack | numpy.hstack |
Matft.concatenate | numpy.concatenate |
*Matft.append | numpy.append |
*Matft.insert | numpy.insert |
*Matft.take | numpy.take |
MfData
andMfStructure
Conversion
MfArray
consists of two main parts, which areMfData
andMfStructure
.MfData
stores the pointer of raw data, the current type (=MfType
), and the "stored" raw type (=StoredType
, onlyFloat
orDouble
). On the other hand,MfStructure
stores theshape
andstrides
property, which represents the current array's structure and are same asNumpy
.
So, when we exploits theMfData
andMfStructure
, changing the array's structure (=MfStructure
) is very efficient. For example, when you have thea
array initialized below;
leta=Matft.arange(start:0,to:27,by:1,shape:[3,3,3])print(a)/*mfarray = [[[ 0, 1, 2],[ 3, 4, 5],[ 6, 7, 8]],[[ 9, 10, 11],[ 12, 13, 14],[ 15, 16, 17]],[[ 18, 19, 20],[ 21, 22, 23],[ 24, 25, 26]]], type=Int, shape=[3, 3, 3]*/
Type conversion
You can convert the current type into many types byastype
method or function.
leta=Matft.arange(start:0,to:27,by:1,shape:[3,3,3],mftype:.Int)print(a)/*mfarray = [[[ -6, -5, -4],[ -3, -2, -1],[ 0, 1, 2]],[[ 3, 4, 5],[ 6, 7, 8],[ 9, 10, 11]],[[ 12, 13, 14],[ 15, 16, 17],[ 18, 19, 20]]], type=Int, shape=[3, 3, 3]*/print(a.astype(.UInt8))/*mfarray = [[[ 250, 251, 252],[ 253, 254, 255],[ 0, 1, 2]],[[ 3, 4, 5],[ 6, 7, 8],[ 9, 10, 11]],[[ 12, 13, 14],[ 15, 16, 17],[ 18, 19, 20]]], type=UInt8, shape=[3, 3, 3]*/print(a.astype(.Float))/*mfarray = [[[ -6.0, -5.0, -4.0],[ -3.0, -2.0, -1.0],[ 0.0, 1.0, 2.0]],[[ 3.0, 4.0, 5.0],[ 6.0, 7.0, 8.0],[ 9.0, 10.0, 11.0]],[[ 12.0, 13.0, 14.0],[ 15.0, 16.0, 17.0],[ 18.0, 19.0, 20.0]]], type=Float, shape=[3, 3, 3]*/
Positive and Negative Indexing
You can extract the slicedMfArrray
by~<
efficiently.
Note that usea[0~<]
instead ofa[:]
to get all elements along axis (ora[Matft.all]
).
print(a[~<1])//same as a[:1] for numpy/*mfarray = [[[ 9, 10, 11], [ 12, 13, 14], [ 15, 16, 17]]], type=Int, shape=[1, 3, 3]*/print(a[1~<3])//same as a[1:3] for numpy/*mfarray = [[[ 9, 10, 11], [ 12, 13, 14], [ 15, 16, 17]],[[ 18, 19, 20], [ 21, 22, 23], [ 24, 25, 26]]], type=Int, shape=[2, 3, 3]*/print(a[~<~<2])//same as a[::2] for numpy//print(a[~<<2]) //alias/*mfarray = [[[ 0, 1, 2], [ 3, 4, 5], [ 6, 7, 8]],[[ 18, 19, 20], [ 21, 22, 23], [ 24, 25, 26]]], type=Int, shape=[2, 3, 3]*/print(a[Matft.all,0])//same as a[:, 0] for numpy/*mfarray = [[ 0, 1, 2], [ 9, 10, 11], [18, 19, 20]], type=Int, shape=[3, 3]*/
print(a[~<-1])/*mfarray = [[[ 0, 1, 2], [ 3, 4, 5], [ 6, 7, 8]],[[ 9, 10, 11], [ 12, 13, 14], [ 15, 16, 17]]], type=Int, shape=[2, 3, 3]*/print(a[-1~<-3])/*mfarray = [], type=Int, shape=[0, 3, 3]*/print(a[Matft.reverse])//print(a[~<~<-1]) //alias//print(a[~<<-1]) //alias/*mfarray = [[[ 18, 19, 20], [ 21, 22, 23], [ 24, 25, 26]],[[ 9, 10, 11], [ 12, 13, 14], [ 15, 16, 17]],[[ 0, 1, 2], [ 3, 4, 5], [ 6, 7, 8]]], type=Int, shape=[3, 3, 3]*/
Of course, you can set the value into the sliced array.
a[~<1]=Matft.arange(start:0,to:81,by:9,shape:[3,3])print(a)/*mfarray = [[[ 0, 9, 18],[ 27, 36, 45],[ 54, 63, 72]],[[ 9, 10, 11],[ 12, 13, 14],[ 15, 16, 17]],[[ 18, 19, 20],[ 21, 22, 23],[ 24, 25, 26]]], type=Int, shape=[3, 3, 3]*/
Boolean Indexing
Matft
supports also boolean indexing! So, you can extract the array with a specific condition you want like this. This feature will be convenient for an image processing. See thenumpy docs for more details.
letimg=Matft.arange(start:-5,to:70,by:1,shape:[5,5,3],mftype:.Float)print(img)/*mfarray = [[[ -5.0, -4.0, -3.0],[ -2.0, -1.0, 0.0],[ 1.0, 2.0, 3.0],[ 4.0, 5.0, 6.0],[ 7.0, 8.0, 9.0]],[[ 10.0, 11.0, 12.0],[ 13.0, 14.0, 15.0],[ 16.0, 17.0, 18.0],[ 19.0, 20.0, 21.0],[ 22.0, 23.0, 24.0]],[[ 25.0, 26.0, 27.0],[ 28.0, 29.0, 30.0],[ 31.0, 32.0, 33.0],[ 34.0, 35.0, 36.0],[ 37.0, 38.0, 39.0]],[[ 40.0, 41.0, 42.0],[ 43.0, 44.0, 45.0],[ 46.0, 47.0, 48.0],[ 49.0, 50.0, 51.0],[ 52.0, 53.0, 54.0]],[[ 55.0, 56.0, 57.0],[ 58.0, 59.0, 60.0],[ 61.0, 62.0, 63.0],[ 64.0, 65.0, 66.0],[ 67.0, 68.0, 69.0]]], type=Float, shape=[5, 5, 3]*/print(img[img<0])/*mfarray = [ -5.0, -4.0, -3.0, -2.0, -1.0], type=Float, shape=[5]*/img[img<0]=MfArray([0])print(img)/*mfarray = [[[ 0.0, 0.0, 0.0],[ 0.0, 0.0, 0.0],[ 1.0, 2.0, 3.0],[ 4.0, 5.0, 6.0],[ 7.0, 8.0, 9.0]],[[ 10.0, 11.0, 12.0],[ 13.0, 14.0, 15.0],[ 16.0, 17.0, 18.0],[ 19.0, 20.0, 21.0],[ 22.0, 23.0, 24.0]],[[ 25.0, 26.0, 27.0],[ 28.0, 29.0, 30.0],[ 31.0, 32.0, 33.0],[ 34.0, 35.0, 36.0],[ 37.0, 38.0, 39.0]],[[ 40.0, 41.0, 42.0],[ 43.0, 44.0, 45.0],[ 46.0, 47.0, 48.0],[ 49.0, 50.0, 51.0],[ 52.0, 53.0, 54.0]],[[ 55.0, 56.0, 57.0],[ 58.0, 59.0, 60.0],[ 61.0, 62.0, 63.0],[ 64.0, 65.0, 66.0],[ 67.0, 68.0, 69.0]]], type=Float, shape=[5, 5, 3]
Fancy Indexing
Furthermore,Matft
supports Fancy Indexing too! See thenumpy docs for more details.
leta=MfArray([[1,2],[3,4],[5,6]])a[MfArray([0,1,2]),MfArray([0,-1,0])]=MfArray([999,888,777])print(a)/*mfarray = [[ 999, 2],[ 3, 888],[ 777, 6]], type=Int, shape=[3, 2]*/a.T[MfArray([0,1,-1]),MfArray([0,1,0])]=MfArray([-999,-888,-777])print(a)/*mfarray = [[ -999, -777],[ 3, -888],[ 777, 6]], type=Int, shape=[3, 2]*/
Synchronizing extractedMfArray
As you can see the above examples,MfArray
hasbase
property (is similar to view in Numpy). Therefore, aMfArray
extracted (Sliced) bypositive and negative indexing is synchronized. Seenumpy doc for more details.
Note that aMfArray
extracted byBoolean Indexing andFancy Indexing isCOPY ofMfArray
of the original one.
leta=Matft.arange(start:0,to:4*4*2,by:1,shape:[4,4,2])letb=a[0~<,1]b[~<<-1]=MfArray([9999])print(a)/*mfarray = [[[ 0, 1],[ 9999, 9999],[ 4, 5],[ 6, 7]],[[ 8, 9],[ 9999, 9999],[ 12, 13],[ 14, 15]],[[ 16, 17],[ 9999, 9999],[ 20, 21],[ 22, 23]],[[ 24, 25],[ 9999, 9999],[ 28, 29],[ 30, 31]]], type=Int, shape=[4, 4, 2]*/
Etc.
The other conversion functions are below;
Matft | Numpy |
---|---|
*#Matft.astype | *numpy.astype |
*#Matft.transpose | *numpy.transpose |
*#Matft.expand_dims | *numpy.expand_dims |
*#Matft.squeeze | *numpy.squeeze |
*#Matft.broadcast_to | *numpy.broadcast_to |
*#Matft.to_contiguous | *numpy.ascontiguousarray |
*#Matft.flatten | *numpy.flatten |
*#Matft.flip | *numpy.flip |
*#Matft.clip | *numpy.clip |
*#Matft.swapaxes | *numpy.swapaxes |
*#Matft.moveaxis | *numpy.moveaxis |
*Matft.roll | numpy.roll |
*Matft.sort | *numpy.sort |
*Matft.argsort | *numpy.argsort |
^MfArray.toArray | ^numpy.ndarray.tolist |
^MfArray.toFlattenArray | n/a |
*Matft.orderedUnique | numpy.unique |
Math operation
Basic mathematic operations are supported inMatft
.
Arithmetic operation
print(a+a)/*mfarray = [[ 0, 2, 4],[ 6, 8, 10],[ 12, 14, 16]], type=Int, shape=[3, 3]*/print(a-a)/*mfarray = [[ 0, 0, 0],[ 0, 0, 0],[ 0, 0, 0]], type=Int, shape=[3, 3]*/print(a*a)/*mfarray = [[ 0, 1, 4],[ 9, 16, 25],[ 36, 49, 64]], type=Int, shape=[3, 3]*/print(a/a)/*mfarray = [[ -nan, 0.99999994, 0.99999994],[ 0.99999994, 0.99999994, 0.99999994],[ 0.99999994, 0.99999994, 0.99999994]], type=Float, shape=[3, 3]*/
Broadcasting
The one of the main features inNumpy
is broadcasting operation. Seenumpy doc for more details.
leta=Matft.arange(start:0,to:3*3,by:1,shape:[3,3])letb=MfArray([-4,10,2]).reshape([3,1])print(a+b)/*mfarray = [[ -4, -3, -2],[ 13, 14, 15],[ 8, 9, 10]], type=Int, shape=[3, 3]*/
Etc.
Matft
has many mathematic functions.
- Math function
Matft | Numpy |
---|---|
#Matft.math.sin | numpy.sin |
Matft.math.asin | numpy.asin |
Matft.math.sinh | numpy.sinh |
Matft.math.asinh | numpy.asinh |
#Matft.math.cos | numpy.cos |
Matft.math.acos | numpy.acos |
Matft.math.cosh | numpy.cosh |
Matft.math.acosh | numpy.acosh |
#Matft.math.tan | numpy.tan |
Matft.math.atan | numpy.atan |
Matft.math.tanh | numpy.tanh |
Matft.math.atanh | numpy.atanh |
Matft.math.sqrt | numpy.sqrt |
Matft.math.rsqrt | numpy.rsqrt |
#Matft.math.exp | numpy.exp |
#Matft.math.log | numpy.log |
Matft.math.log2 | numpy.log2 |
Matft.math.log10 | numpy.log10 |
*Matft.math.ceil | numpy.ceil |
*Matft.math.floor | numpy.floor |
*Matft.math.trunc | numpy.trunc |
*Matft.math.nearest | numpy.nearest |
*Matft.math.round | numpy.round |
#Matft.math.abs | numpy.abs |
Matft.math.reciprocal | numpy.reciprocal |
#Matft.math.power | numpy.power |
Matft.math.arctan2 | numpy.arctan2 |
Matft.math.square | numpy.square |
Matft.math.sign | numpy.sign |
- Statistics function
Matft | Numpy |
---|---|
*Matft.stats.mean | *numpy.mean |
*Matft.stats.max | *numpy.max |
*Matft.stats.argmax | *numpy.argmax |
*Matft.stats.min | *numpy.min |
*Matft.stats.argmin | *numpy.argmin |
*Matft.stats.sum | *numpy.sum |
Matft.stats.maximum | numpy.maximum |
Matft.stats.minimum | numpy.minimum |
*Matft.stats.sumsqrt | n/a |
*Matft.stats.squaresum | n/a |
*Matft.stats.cumsum | *numpy.cumsum |
- Random function
Matft | Numpy |
---|---|
Matft.random.rand | numpy.random.rand |
Matft.random.randint | numpy.random.randint |
- Linear algebra
Matft | Numpy |
---|---|
Matft.linalg.solve | numpy.linalg.solve |
Matft.linalg.inv | numpy.linalg.inv |
Matft.linalg.det | numpy.linalg.det |
Matft.linalg.eigen | numpy.linalg.eig |
Matft.linalg.svd | numpy.linalg.svd |
Matft.linalg.pinv | numpy.linalg.pinv |
Matft.linalg.polar_left | scipy.linalg.polar |
Matft.linalg.polar_right | scipy.linalg.polar |
Matft.linalg.normlp_vec | scipy.linalg.norm |
Matft.linalg.normfro_mat | scipy.linalg.norm |
Matft.linalg.normnuc_mat | scipy.linalg.norm |
Advanced operation
Matft
has complex and image processing functions too!!!!
So,Matft
will be very useful for the signal and image processing!
But these functions are beta versions currently (on 22/08/08).
Complex operation
You can operate the complex values like the real ones.
letreal=Matft.arange(start:0,to:16,by:1).reshape([2,2,4])letimag=Matft.arange(start:0,to:-16,by:-1).reshape([2,2,4])leta=MfArray(real:real,imag:imag)print(a)/*mfarray = [[[ 0 +0j, 1 -1j, 2 -2j, 3 -3j],[ 4 -4j, 5 -5j, 6 -6j, 7 -7j]],[[ 8 -8j, 9 -9j, 10 -10j, 11 -11j],[ 12 -12j, 13 -13j, 14 -14j, 15 -15j]]], type=Int, shape=[2, 2, 4]*/print(a+a)/*mfarray = [[[ 0 +0j, 2 -2j, 4 -4j, 6 -6j],[ 8 -8j, 10 -10j, 12 -12j, 14 -14j]],[[ 16 -16j, 18 -18j, 20 -20j, 22 -22j],[ 24 -24j, 26 -26j, 28 -28j, 30 -30j]]], type=Int, shape=[2, 2, 4]*/print(Matft.complex.angle(a))/*mfarray = [[[ -0.0, -0.7853982, -0.7853982, -0.7853982],[ -0.7853982, -0.7853982, -0.7853982, -0.7853982]],[[ -0.7853982, -0.7853982, -0.7853982, -0.7853982],[ -0.7853982, -0.7853982, -0.7853982, -0.7853982]]], type=Float, shape=[2, 2, 4]*/print(Matft.complex.conjugate(a))/*mfarray = [[[ 0 +0j, 1 +1j, 2 +2j, 3 +3j],[ 4 +4j, 5 +5j, 6 +6j, 7 +7j]],[[ 8 +8j, 9 +9j, 10 +10j, 11 +11j],[ 12 +12j, 13 +13j, 14 +14j, 15 +15j]]], type=Int, shape=[2, 2, 4]*/
Matft | Numpy |
---|---|
Matft.complex.angle | numpy.angle |
Matft.complex.conjugate | numpy.conj / numpy.conjugate |
Matft.complex.abs | numpy.abs / numpy.absolute |
Image operation
Conversion from/toCGImage
The conversion functions betweenMfArray
andCGImage
are ready. So, you can implement the complex image operation code
very easily! For example, if you use theMatft
's indexing feature,...
importUIKitimportMatftimportAccelerateimportCoreGraphicsimportCoreGraphics.CGBitmapContextclassViewController:UIViewController{@IBOutletweakvaroriginalImageView:UIImageView!@IBOutletweakvarreverseImageView:UIImageView!@IBOutletweakvargrayreverseImageView:UIImageView!@IBOutletweakvarswapImageView:UIImageView!overridefuncviewDidLoad(){super.viewDidLoad()self.originalImageView.image=UIImage(named:"rena.jpeg")self.reverseImageView.image=UIImage(named:"rena.jpeg")self.grayreverseImageView.image=self.convertToGrayScale(image:UIImage(named:"rena.jpeg")!)self.swapImageView.image=UIImage(named:"rena.jpeg")self.reverse()self.grayreverse()self.swapchannel()self.resize()}funcreverse(){varimage=Matft.image.cgimage2mfarray(self.reverseImageView.image!.cgImage!)// reverseimage=image[Matft.reverse]// same as image[~<<-1]self.reverseImageView.image=UIImage(cgImage:Matft.image.mfarray2cgimage(image))}funcswapchannel(){varimage=Matft.image.cgimage2mfarray(self.swapImageView.image!.cgImage!)// swap channelimage=image[Matft.all,Matft.all,MfArray([1,0,2,3])]// same as image[0~<, 0~<, MfArray([1,0,2,3])]self.swapImageView.image=UIImage(cgImage:Matft.image.mfarray2cgimage(image))}funcgrayreverse(){varimage=Matft.image.cgimage2mfarray(self.grayreverseImageView.image!.cgImage!,mftype:.UInt8)// reverseimage=image[Matft.reverse]// same as image[~<<-1]self.grayreverseImageView.image=UIImage(cgImage:Matft.image.mfarray2cgimage(image))}funcconvertToGrayScale(image:UIImage)->UIImage{//let gray_mfarray = (Matft.image.color(Matft.image.cgimage2mfarray(image.cgImage!)) * Float(255)).astype(.UInt8)letgray_mfarray=Matft.image.color(Matft.image.cgimage2mfarray(image.cgImage!))returnUIImage(cgImage:Matft.image.mfarray2cgimage(gray_mfarray))}funcresize(){varimage=Matft.image.cgimage2mfarray(self.swapImageView.image!.cgImage!)// resizeimage=Matft.image.resize(image,width:300,height:300)self.swapImageView.image=UIImage(cgImage:Matft.image.mfarray2cgimage(image))//self.swapImageView.frame = CGRect(x: 0, y: 0, width: 300, height: 300)}}
Awesome!
Affine
// CGImage to MfArrayletrgb=Matft.image.cgimage2mfarray(uiimage.cgImage!)// Convert RGBA into Gray Imageletgray=Matft.image.color(rgb)// Resizeletrgb_resize=Matft.image.resize(rgb,width:150,height:150)letgray_resize=Matft.image.resize(gray,width:300,height:300)letrbg_rotated=Matft.image.warpAffine(rgb_resize,matrix:MfArray([[0,1,1],[1,0,0]]as[[Float]]),width:150,height:150)letgray_rotated=Matft.image.warpAffine(gray_resize,matrix:MfArray([[0,1,1],[1,0,0]]as[[Float]]),width:150,height:150)Matft.image.mfarray2cgimage(rbg_rotated)
Matft | Numpy |
---|---|
Matft.image.cgimage2mfarray | N/A |
Matft.image.mfarray2cgimage | N/A |
Matft | OpenCV |
---|---|
Matft.image.color | cv2.cvtColor |
Matft.image.resize | cv2.resize |
Matft.image.warpAffine | cv2.warpAffine |
Conclusion
I introduced theMatft
in this post.Matft
has many features from the basic mathematic array operations to the complicated operations such like boolean indexing, fancy indexing, complex data, image data and so on.
I hopeMatft
will be useful for your project and help your project more efficient!
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse