jjjkkkjjj / Matft

Numpy-like library in swift. (Multi-dimensional Array, ndarray, matrix and vector library)
BSD 3-Clause "New" or "Revised" License
134 stars 21 forks source link
complex-numbers image image-processing math matrix-library ndarray ndimensional-arrays numpy signal-processing swift

Matft

SwiftPM compatible CocoaPods compatible Carthage compatible license

Matft is Numpy-like library in Swift. Function name and usage is similar to Numpy.

INFO: Support Complex!!

Note: You can use Protocol version(beta version) too.

Feature & Usage

...etc.

See Function List for all functions.

Declaration

MfArray

MfType

Subscription

MfSlice

You can set MfSlice (see below's list) to subscript.

(Positive) Indexing

Negative Indexing

Boolean Indexing

Fancy Indexing

View

Complex

Matft supports Complex!!

But this is beta version. so, any bug may be ocurred.

Please report me by issue! (Progres

TODO

let real = Matft.arange(start: 0, to: 16, by: 1).reshape([2,2,4])
let imag = Matft.arange(start: 0, to: -16, by: -1).reshape([2,2,4])
let a = 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]
*/

Image

You can acheive an image processing by Matft! (Beta version) Please refer to the example here.

@IBOutlet weak var originalImageView: UIImageView!
@IBOutlet weak var reverseImageView: UIImageView!
@IBOutlet weak var swapImageView: UIImageView!

func reverse(){
    var image = Matft.image.cgimage2mfarray(self.reverseImageView.image!.cgImage!)

    // reverse
    image = image[Matft.reverse] // same as image[~<<-1]
    self.reverseImageView.image = UIImage(cgImage: Matft.image.mfarray2cgimage(image))
}

func swapchannel(){
    var image = Matft.image.cgimage2mfarray(self.swapImageView.image!.cgImage!)

    // swap channel
    image = 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))
}

For more complex conversion, see OpenCV code.

Screen Shot 2022-07-19 at 21 09 02

Function List

Below is Matft's function list. As I mentioned above, almost functions are similar to Numpy. Also, these function use Accelerate framework inside, the perfomance may keep high.

* means method function exists too. Shortly, you can use a.shallowcopy() where a is MfArray.

^ means method function only. Shortly, you can use a.tolist() not Matft.tolist where a is MfArray.

# means support complex operation

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
^MfArray.item ^numpy.ndarray.item
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
^MfArray.toMLMultiArray n/a
*Matft.orderedUnique numpy.unique
Matft Numpy
Matft.file.loadtxt numpy.loadtxt
Matft.file.genfromtxt numpy.genfromtxt
Matft.file.savetxt numpy.savetxt
Matft Numpy
#Matft.add
+
numpy.add
+
#Matft.sub
-
numpy.sub
-
#Matft.div
/
numpy.div
.
#Matft.mul
*
numpy.multiply
*
Matft.inner
*+
numpy.inner
n/a
Matft.cross
*^
numpy.cross
n/a
Matft.matmul
*&  
numpy.matmul
@ 
Matft.dot    numpy.dot  
Matft.equal
===
numpy.equal
==
Matft.not_equal
!==
numpy.not_equal
!=
Matft.less
<
numpy.less
<
Matft.less_equal
<=
numpy.less_equal
<=
Matft.greater
>
numpy.greater
>
Matft.greater_equal
>=
numpy.greater_equal
>=
#Matft.allEqual
==
numpy.array_equal
n/a
#Matft.neg
-
numpy.negative
-
Matft Numpy
*#Matft.ufuncReduce
e.g.) Matft.ufuncReduce(a, Matft.add)
numpy.add.reduce
e.g.) numpy.add.reduce(a)
*#Matft.ufuncAccumulate
e.g.) Matft.ufuncAccumulate(a, Matft.add)
numpy.add.accumulate
e.g.) numpy.add.accumulate(a)
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
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
Matft Numpy
Matft.random.rand numpy.random.rand
Matft.random.randint numpy.random.randint
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
Matft Numpy
Matft.complex.angle numpy.angle
Matft.complex.conjugate numpy.conj / numpy.conjugate
Matft.complex.abs numpy.abs / numpy.absolute
Matft Numpy
Matft.fft.rfft numpy.fft.rfft
Matft.fft.irfft numpy.fft.irfft

Matft supports only natural cubic spline. I'll implement other boundary condition later.

Matft Scipy
Matft.interp1d.cubicSpline scipy.interpolation.CubicSpline
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

Performance

I use Accelerate framework, so all of MfArray operation may keep high performance.

let a = Matft.arange(start: 0, to: 10*10*10*10*10*10, by: 1, shape: [10,10,10,10,10,10])
let aneg = Matft.arange(start: 0, to: -10*10*10*10*10*10, by: -1, shape: [10,10,10,10,10,10])
let aT = a.T
let b = a.transpose(axes: [0,3,4,2,1,5])
let c = a.transpose(axes: [1,2,3,4,5,0])
let posb = a > 0
import numpy as np

a = np.arange(10**6).reshape((10,10,10,10,10,10))
aneg = np.arange(0, -10**6, -1).reshape((10,10,10,10,10,10))
aT = a.T
b = a.transpose((0,3,4,2,1,5))
c = a.transpose((1,2,3,4,5,0))
posb = a > 0
Matft time Numpy time
let _ = a+aneg 596μs a+aneg 1.04ms
let _ = b+aT 4.46ms b+aT 4.31ms
let _ = c+aT 5.31ms c+aT 2.92ms
Matft time Numpy time
let _ = Matft.math.sin(a) 2.14ms np.sin(a) 14.7ms
let _ = Matft.math.sin(b) 7.02ms np.sin(b) 15.8ms
let _ = Matft.math.sign(a) 3.09ms np.sign(a) 1.37ms
let _ = Matft.math.sign(b) 8.33ms np.sign(b) 1.42ms
Matft time Numpy time
let _ = a > 0 4.63ms a > 0 855μs
let _ = a > b 17.8ms a > b 1.83ms
let _ = a === 0 4.65ms a == 0 603μs
let _ = a === b 19.7ms a == b 1.78ms
Matft time Numpy time
let _ = a[posb] 1.21ms a[posb] 1.29ms

Matft achieved almost same performance as Numpy!!!

※Swift's performance test was conducted in release mode

However, as you can see the above table, Matft's boolean operation is toooooooo slow...(Issue #18)

So, a pull request is very welcome!!

Installation

SwiftPM

Important!!! the below installation is outdated. Please install Matft via swiftPM!!!

Carthage

CocoaPods

Contact

Feel free to ask this project or anything via junnosuke.kado.git@gmail.com