mat

Matft

Numpy-like library in swift. (Multi-dimensional Array, ndarray, matrix and vector library)

Showing:

Popularity

Downloads/wk

0

GitHub Stars

37

Maintenance

Last Commit

1mo ago

Contributors

2

Package

Dependencies

0

License

BSD-3-Clause

Categories

Readme

Matft

SwiftPM compatible CocoaPods compatible Carthage compatible license

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

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

Feature & Usage

  • Many types

  • Pretty print

  • Indexing

    • Positive
    • Negative
    • Boolean
    • Fancy
  • Slicing

    • Start / To / By
    • New Axis
  • View

    • Assignment
  • Conversion

    • Broadcast
    • Transpose
    • Reshape
    • Astype
  • Univarsal function reduction

  • Mathematic

    • Arithmetic
    • Statistic
    • Linear Algebra

...etc.

See Function List for all functions.

Declaration

MfArray

  • The MfArray such like a numpy.ndarray

    let a = MfArray([[[ -8,  -7,  -6,  -5],
                      [ -4,  -3,  -2,  -1]],
            
                     [[ 0,  1,  2,  3],
                      [ 4,  5,  6,  7]]])
    let aa = Matft.arange(start: -8, to: 8, by: 1, shape: [2,2,4])
    print(a)
    print(aa)
    /*
    mfarray = 
    [[[ -8,     -7,     -6,     -5],
    [   -4,     -3,     -2,     -1]],
    
    [[  0,      1,      2,      3],
    [   4,      5,      6,      7]]], type=Int, shape=[2, 2, 4]
    mfarray = 
    [[[ -8,     -7,     -6,     -5],
    [   -4,     -3,     -2,     -1]],
    
    [[  0,      1,      2,      3],
    [   4,      5,      6,      7]]], type=Int, shape=[2, 2, 4]
    */
    

MfType

  • You can pass MfType as MfArray's argument mftype: .Hoge . It is similar to dtype.

    ※Note that stored data type will be Float or Double only even if you set MfType.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's list is below

    public enum MfType: Int{
        case None // Unsupportted
        case Bool
        case UInt8
        case UInt16
        case UInt32
        case UInt64
        case UInt
        case Int8
        case Int16
        case Int32
        case Int64
        case Int
        case Float
        case Double
        case Object // Unsupported
    }
    
  • Also, you can convert MfType easily using astype

    let a = MfArray([[[ -8,  -7,  -6,  -5],
                      [ -4,  -3,  -2,  -1]],
            
                     [[ 0,  1,  2,  3],
                      [ 4,  5,  6,  7]]])
    print(a)//See above. if mftype is not passed, MfArray infer MfType. In this example, it's MfType.Int
    
    let a = MfArray([[[ -8,  -7,  -6,  -5],
                      [ -4,  -3,  -2,  -1]],
                
                     [[ 0,  1,  2,  3],
                      [ 4,  5,  6,  7]]], mftype: .Float)
    print(a)
    /*
    mfarray = 
    [[[ -8.0,       -7.0,       -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]]], type=Float, shape=[2, 2, 4]
    */
    let aa = MfArray([[[ -8,  -7,  -6,  -5],
                      [ -4,  -3,  -2,  -1]],
                
                     [[ 0,  1,  2,  3],
                      [ 4,  5,  6,  7]]], mftype: .UInt)
    print(aa)
    /*
    mfarray = 
    [[[ 4294967288,     4294967289,     4294967290,     4294967291],
    [   4294967292,     4294967293,     4294967294,     4294967295]],
    
    [[  0,      1,      2,      3],
    [   4,      5,      6,      7]]], type=UInt, shape=[2, 2, 4]
    */
    //Above output is same as numpy!
    /*
    >>> np.arange(-8, 8, dtype=np.uint32).reshape(2,2,4)
    array([[[4294967288, 4294967289, 4294967290, 4294967291],
            [4294967292, 4294967293, 4294967294, 4294967295]],
    
           [[         0,          1,          2,          3],
            [         4,          5,          6,          7]]], dtype=uint32)
    */
    
    print(aa.astype(.Float))
    /*
    mfarray = 
    [[[ -8.0,       -7.0,       -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]]], type=Float, shape=[2, 2, 4]
    */
    

Subscription

MfSlice

  • You can access specific data using subscript.

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

  • MfSlice(start: Int? = nil, to: Int? = nil, by: Int = 1)
    
  • Matft.newaxis
    
  • ~< //this is prefix, postfix and infix operator. same as python's slice, ":"
    

(Positive) Indexing

  • Normal indexing

    let a = 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]
    */
    print(a[2,1,0])
    // 21
    

    Slicing

  • If you replace : with ~<, you can get sliced mfarray. Note that use a[0~<] instead of a[:] to get all elements along axis.

    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]
    */
    

Negative Indexing

  • Negative indexing is also available That's implementation was hardest for me...

    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[~<~<-1])
    //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]*/
    

Boolean Indexing

  • You can use boolean indexing.

    Caution! I don't check performance, so this boolean indexing may be slow

    Unfortunately, Matft is too slower than numpy...

    (numpy is 1ms, Matft is 7ms...)

    let img = MfArray([[1, 2, 3],
                                   [4, 5, 6],
                                   [7, 8, 9]], mftype: .UInt8)
    img[img > 3] = MfArray([10], mftype: .UInt8)
    print(img)
    /*
    mfarray = 
    [[  1,      2,      3],
    [   10,     10,     10],
    [   10,     10,     10]], type=UInt8, shape=[3, 3]
    */
    

Fancy Indexing

  • You can use fancy indexing!!!

    let a = 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]
    */
    

View

  • Note that returned subscripted mfarray will have base property (is similar to view in Numpy). See numpy doc in detail.

    let a = Matft.arange(start: 0, to: 4*4*2, by: 1, shape: [4,4,2])
                
    let b = a[0~<, 1]
    b[~<<-1] = MfArray([9999]) // cannot pass Int directly such like 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]
    */
    

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.

  • Creation
MatftNumpy
*Matft.shallowcopy*numpy.copy
*Matft.deepcopycopy.deepcopy
Matft.numsnumpy.ones * N
Matft.nums_likenumpy.ones_like * N
Matft.arangenumpy.arange
Matft.eyenumpy.eye
Matft.diagnumpy.diag
Matft.vstacknumpy.vstack
Matft.hstacknumpy.hstack
Matft.concatenatenumpy.concatenate
*Matft.appendnumpy.append
*Matft.insertnumpy.insert
*Matft.takenumpy.take
  • Conversion
MatftNumpy
*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.conv_order*numpy.ascontiguousarray
*Matft.flatten*numpy.flatten
*Matft.flip*numpy.flip
*Matft.clip*numpy.clip
*Matft.swapaxes*numpy.swapaxes
*Matft.moveaxis*numpy.moveaxis
*Matft.sort*numpy.sort
*Matft.argsort*numpy.argsort
^MfArray.toArray^numpy.ndarray.tolist
^MfArray.toFlattenArrayn/a
*Matft.orderedUniquenumpy.unique
  • File
MatftNumpy
Matft.file.loadtxtnumpy.loadtxt
Matft.file.genfromtxtnumpy.genfromtxt
Matft.file.savetxtnumpy.savetxt
  • Operation

    Line 2 is infix (prefix) operator.

MatftNumpy
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.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
-
  • Universal Fucntion Reduction
MatftNumpy
*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)
  • Math function
MatftNumpy
Matft.math.sinnumpy.sin
Matft.math.asinnumpy.asin
Matft.math.sinhnumpy.sinh
Matft.math.asinhnumpy.asinh
Matft.math.sinnumpy.cos
Matft.math.acosnumpy.acos
Matft.math.coshnumpy.cosh
Matft.math.acoshnumpy.acosh
Matft.math.tannumpy.tan
Matft.math.atannumpy.atan
Matft.math.tanhnumpy.tanh
Matft.math.atanhnumpy.atanh
Matft.math.sqrtnumpy.sqrt
Matft.math.rsqrtnumpy.rsqrt
Matft.math.expnumpy.exp
Matft.math.lognumpy.log
Matft.math.log2numpy.log2
Matft.math.log10numpy.log10
*Matft.math.ceilnumpy.ceil
*Matft.math.floornumpy.floor
*Matft.math.truncnumpy.trunc
*Matft.math.nearestnumpy.nearest
*Matft.math.roundnumpy.round
Matft.math.absnumpy.abs
Matft.math.reciprocalnumpy.reciprocal
Matft.math.powernumpy.power
Matft.math.squarenumpy.square
Matft.math.signnumpy.sign
  • Statistics function
MatftNumpy
*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.maximumnumpy.maximum
Matft.stats.minimumnumpy.minimum
*Matft.stats.sumsqrtn/a
*Matft.stats.squaresumn/a
*Matft.stats.cumsum*numpy.cumsum
  • Random function
MatftNumpy
Matft.random.randnumpy.random.rand
Matft.random.randintnumpy.random.randint
  • Linear algebra
MatftNumpy
Matft.linalg.solvenumpy.linalg.solve
Matft.linalg.invnumpy.linalg.inv
Matft.linalg.detnumpy.linalg.det
Matft.linalg.eigennumpy.linalg.eig
Matft.linalg.svdnumpy.linalg.svd
Matft.linalg.pinvnumpy.linalg.pinv
Matft.linalg.polar_leftscipy.linalg.polar
Matft.linalg.polar_rightscipy.linalg.polar
Matft.linalg.normlp_vecscipy.linalg.norm
Matft.linalg.normfro_matscipy.linalg.norm
Matft.linalg.normnuc_matscipy.linalg.norm
  • Interpolation

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

MatftNumpy
Matft.interp1d.cubicSplinescipy.interpolation.CubicSpline

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
  • Arithmetic test
MatfttimeNumpytime
let _ = a+aneg863μsa+aneg1.04ms
let _ = b+aT4.47msb+aT4.31ms
let _ = c+aT5.30msc+aT2.92ms
  • Math test
MatfttimeNumpytime
let _ = Matft.math.sin(a)1.80msnp.sin(a)14.7ms
let _ = Matft.math.sin(b)8.24msnp.sin(b)15.8ms
let _ = Matft.math.sign(a)30.4msnp.sign(a)1.37ms
let _ = Matft.math.sign(b)35.6msnp.sign(b)1.42ms
  • Bool test
MatfttimeNumpytime
let _ = a > 07.27msa > 0855μs
let _ = a > b13.3msa > b1.83ms
let _ = a === 09.91msa == 0603μs
let _ = a === b22.3msa == b1.78ms
  • Indexing test
MatfttimeNumpytime
let _ = a[posb]1.07msa[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

Caution: I strongly recommend to use SwiftPM!!

SwiftPM

  • Import
    • Project > Build Setting > + Build Setting
    • Select Rules select
  • Update
    • File >Swift Packages >Update to Latest Package versions update

Carthage

  • Set Cartfile

    echo 'github "jjjkkkjjj/Matft"' > Cartfile
    carthage update ###or append '--platform ios'
    
  • Import Matft.framework made by above process to your project

CocoaPods

  • Create Podfile (Skip if you have already done)

    pod init
    
  • Write pod 'Matft' in Podfile such like below

    target 'your project' do
      pod 'Matft'
    end
    
  • Install Matft

    pod install
    

Contact

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

Rate & Review

Great Documentation0
Easy to Use0
Performant0
Highly Customizable0
Bleeding Edge0
Responsive Maintainers0
Poor Documentation0
Hard to Use0
Slow0
Buggy0
Abandoned0
Unwelcoming Community0
100