20200515のSwiftに関する記事は11件です。

SwiftでN次元行列演算ライブラリを作ってみた

はじめに

初投稿です.
タイトルの通りなのですが,SwiftでN次元行列演算ライブラリMatftを作ってみました.(Mat rix演算をする・Swi ftで,の略でMatftです笑)

事の発端は,会社の先輩の「Swiftで3行3列の逆行列を求めるコードを書いてほしい」という一言でした.
SwiftにはPythonのNumpyのようなN次元行列演算ライブラリがあるだろうと思ったのですが,調べると意外にもないんですよね...
公式のAccelerateは使い勝手悪そうだし,有名らしいsurgeも2次元まで?みたいでした.そんなこんなで,せっかくなので自作のN次元行列演算ライブラリを作ってみようと思いました.(3行3列の逆行列を求めるコードに対して,完全にオーバースペックですが笑)

さらにそんなこんなで,Matftができました.そしてせっかくなので共有してみようということで,現在に至ります.

概要

基本的にはPythonのNumpyにならって作成したので,関数名や使い方はNumpyとほぼ同じです.

宣言

宣言はndarrayなるMfArrayで多次元配列を生成します.

let a = MfArray([[[ -8,  -7,  -6,  -5],
                  [ -4,  -3,  -2,  -1]],

                 [[ 0,  1,  2,  3],
                  [ 4,  5,  6,  7]]])
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]
*/

いろいろな型に対応させたかったので,それなりの型を用意しました.dtypeならぬMfTypeです.

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)

型一覧は以下のEnum型で定義しました.
※実を言うと,裏ではFloatDoubleで保存しているので,UIntなんかは値が大きいとオーバーフローします.ただ,実用上は問題ないと思います.

 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
}

Indexing

Numpyでいうa[:, ::-1]のようなスライスも~で実装しました.
-1のような負のインデックスも実装しました.(これが一番苦労したかもしれません...)

let a = Matft.mfarray.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
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[-1~-3])
/*
mfarray = 
    [], type=Int, shape=[0, 3, 3]
*/
print(a[~~-1])
/*
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]*/

その他関数一覧

ここからは,具体的な関数一覧です.主な計算はAccelerateに任せているので,計算時間はある程度担保されていると思います.

  • 生成系
Matft Numpy
Matft.mfarray.shallowcopy numpy.copy
Matft.mfarray.deepcopy copy.deepcopy
Matft.mfarray.nums numpy.ones * N
Matft.mfarray.arange numpy.arange
Matft.mfarray.eye numpy.eye
Matft.mfarray.diag numpy.diag
Matft.mfarray.vstack numpy.vstack
Matft.mfarray.hstack numpy.hstack
Matft.mfarray.concatenate numpy.concatenate
  • 変換系
Matft Numpy
Matft.mfarray.astype numpy.astype
Matft.mfarray.transpose numpy.transpose
Matft.mfarray.expand_dims numpy.expand_dims
Matft.mfarray.squeeze numpy.squeeze
Matft.mfarray.broadcast_to numpy.broadcast_to
Matft.mfarray.conv_order numpy.ascontiguousarray
Matft.mfarray.flatten numpy.flatten
Matft.mfarray.flip numpy.flip
Matft.mfarray.swapaxes numpy.swapaxes
Matft.mfarray.moveaxis numpy.moveaxis
Matft.mfarray.sort numpy.sort
Matft.mfarray.argsort numpy.argsort
  • ファイル関係 saveが未完成です.
Matft Numpy
Matft.mfarray.file.loadtxt numpy.loadtxt
Matft.mfarray.file.genfromtxt numpy.genfromtxt
  • 演算系
Matft Numpy
Matft.mfarray.add numpy.add
Matft.mfarray.sub numpy.sub
Matft.mfarray.div numpy.div
Matft.mfarray.mul numpy.multiply
Matft.mfarray.inner numpy.inner
Matft.mfarray.cross numpy.cross
Matft.mfarray.equal numpy.equal
Matft.mfarray.allEqual numpy.array_equal
Matft.mfarray.neg numpy.negative
  • 初等関数系
Matft Numpy
Matft.mfarray.math.sin numpy.sin
Matft.mfarray.math.asin numpy.asin
Matft.mfarray.math.sinh numpy.sinh
Matft.mfarray.math.asinh numpy.asinh
Matft.mfarray.math.sin numpy.cos
Matft.mfarray.math.acos numpy.acos
Matft.mfarray.math.cosh numpy.cosh
Matft.mfarray.math.acosh numpy.acosh
Matft.mfarray.math.tan numpy.tan
Matft.mfarray.math.atan numpy.atan
Matft.mfarray.math.tanh numpy.tanh
Matft.mfarray.math.atanh numpy.atanh

面倒なので,省略します...笑
ここを見てください

  • 高階関数系
Matft Numpy
Matft.mfarray.stats.mean numpy.mean
Matft.mfarray.math.max numpy.max
Matft.mfarray.math.argmax numpy.argmax
Matft.mfarray.math.min numpy.min
Matft.mfarray.math.argmin numpy.argmin
Matft.mfarray.math.sum numpy.sum
  • 線形代数系
Matft Numpy
Matft.mfarray.linalg.solve numpy.linalg.solve
Matft.mfarray.linalg.inv numpy.linalg.inv
Matft.mfarray.linalg.det numpy.linalg.det
Matft.mfarray.linalg.eigen numpy.linalg.eig
Matft.mfarray.linalg.svd numpy.linalg.svd
Matft.mfarray.linalg.polar_left scipy.linalg.polar
Matft.mfarray.linalg.polar_right scipy.linalg.polar

インストール

SwiftPMとCocoaPodに対応しました.

SwiftPM

  • Import
    • Project > Build Setting > + Build Setting
    • 適宜選択 select
  • アップデート
    • File >Swift Packages >Update to Latest Package versions update

CocoaPods

  • Podfile作成 (すでにある場合は無視)
  pod init
  • pod 'Matft'をPodfileに追記
  target 'your project' do
    pod 'Matft'
  end
  • インストール
  pod install

Performance

Accelerateに任せているので,計算は担保されていると言いましたが,足し算だけ速度を計算してみました.
時間があれば他の関数も調べます...

case Matft Numpy
1 1.14ms 962 µs
2 4.20ms 5.68 ms
3 4.17ms 3.92 ms
  • Matft
func testPefAdd1() {
        do{
            let a = Matft.mfarray.arange(start: 0, to: 10*10*10*10*10*10, by: 1, shape: [10,10,10,10,10,10])
            let b = Matft.mfarray.arange(start: 0, to: -10*10*10*10*10*10, by: -1, shape: [10,10,10,10,10,10])

            self.measure {
                let _ = a+b
            }
            /*
             '-[MatftTests.ArithmeticPefTests testPefAdd1]' measured [Time, seconds] average: 0.001, relative standard deviation: 23.418%, values: [0.001707, 0.001141, 0.000999, 0.000969, 0.001029, 0.000979, 0.001031, 0.000986, 0.000963, 0.001631]
            1.14ms
             */
        }
    }

    func testPefAdd2(){
        do{
            let a = Matft.mfarray.arange(start: 0, to: 10*10*10*10*10*10, by: 1, shape: [10,10,10,10,10,10])
            let b = a.transpose(axes: [0,3,4,2,1,5])
            let c = a.T

            self.measure {
                let _ = b+c
            }
            /*
             '-[MatftTests.ArithmeticPefTests testPefAdd2]' measured [Time, seconds] average: 0.004, relative standard deviation: 5.842%, values: [0.004680, 0.003993, 0.004159, 0.004564, 0.003955, 0.004200, 0.003998, 0.004317, 0.003919, 0.004248]
            4.20ms
             */
        }
    }

    func testPefAdd3(){
        do{
            let a = Matft.mfarray.arange(start: 0, to: 10*10*10*10*10*10, by: 1, shape: [10,10,10,10,10,10])
            let b = a.transpose(axes: [1,2,3,4,5,0])
            let c = a.T

            self.measure {
                let _ = b+c
            }
            /*
             '-[MatftTests.ArithmeticPefTests testPefAdd3]' measured [Time, seconds] average: 0.004, relative standard deviation: 16.815%, values: [0.004906, 0.003785, 0.003702, 0.005981, 0.004261, 0.003665, 0.004083, 0.003654, 0.003836, 0.003874]
            4.17ms
             */
        }
}
  • Numpy
In [1]:
import numpy as np
#import timeit

a = np.arange(10**6).reshape((10,10,10,10,10,10))
b = np.arange(0, -10**6, -1).reshape((10,10,10,10,10,10))

#timeit.timeit("b+c", repeat=10, globals=globals())
%timeit -n 10 a+b
962 µs ± 273 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [2]:
a = np.arange(10**6).reshape((10,10,10,10,10,10))
b = a.transpose((0,3,4,2,1,5))
c = a.T
#timeit.timeit("b+c", repeat=10, globals=globals())
%timeit -n 10 b+c
5.68 ms ± 1.45 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [3]:
a = np.arange(10**6).reshape((10,10,10,10,10,10))
b = a.transpose((1,2,3,4,5,0))
c = a.T
#timeit.timeit("b+c", repeat=10, globals=globals())
%timeit -n 10 b+c
3.92 ms ± 897 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

個人的に気に入っているところ

  • 名前
  • 複数型対応
  • Numpyぽさ

最後に

気まぐれで作成しましたが,思ったよりいい感じのものができました.ただただ僕の検索力不足なだけで,より良いライブラリがあるかもしれませんが,是非試していただけると嬉しいです.(環境依存のチェックができていませんので,是非お願いします...)
とにもかくにもいい勉強になりました.ありがとうございました.

参考

numpy
scipy
Accelerate
SwiftでNDArray書く(テストケースを参考にさせていただきました.)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SwiftでN次元行列演算ライブラリMatftを作ってみた

はじめに

初投稿です.
タイトルの通りなのですが,SwiftでN次元行列演算ライブラリMatftを作ってみました.(Mat rix演算をする・Swi ftで,の略でMatftです笑)

事の発端は,会社の先輩の「Swiftで3行3列の逆行列を求めるコードを書いてほしい」という一言でした.
SwiftにはPythonのNumpyのようなN次元行列演算ライブラリがあるだろうと思ったのですが,調べると意外にもないんですよね...
公式のAccelerateは使い勝手悪そうだし,有名らしいsurgeも2次元まで?みたいでした.そんなこんなで,せっかくなので自作のN次元行列演算ライブラリを作ってみようと思いました.(3行3列の逆行列を求めるコードに対して,完全にオーバースペックですが笑)

さらにそんなこんなで,Matftができました.そしてせっかくなので共有してみようということで,現在に至ります.

概要

基本的にはPythonのNumpyにならって作成したので,関数名や使い方はNumpyとほぼ同じです.

宣言

宣言はndarrayなるMfArrayで多次元配列を生成します.

let a = MfArray([[[ -8,  -7,  -6,  -5],
                  [ -4,  -3,  -2,  -1]],

                 [[ 0,  1,  2,  3],
                  [ 4,  5,  6,  7]]])
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]
*/

いろいろな型に対応させたかったので,それなりの型を用意しました.dtypeならぬMfTypeです.

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)

型一覧は以下のEnum型で定義しました.
※実を言うと,裏ではFloatDoubleで保存しているので,UIntなんかは値が大きいとオーバーフローします.ただ,実用上は問題ないと思います.

 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
}

Indexing

Numpyでいうa[:, ::-1]のようなスライスも~で実装しました.
-1のような負のインデックスも実装しました.(これが一番苦労したかもしれません...)

let a = Matft.mfarray.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
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[-1~-3])
/*
mfarray = 
    [], type=Int, shape=[0, 3, 3]
*/
print(a[~~-1])
/*
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]*/

その他関数一覧

ここからは,具体的な関数一覧です.主な計算はAccelerateに任せているので,計算時間はある程度担保されていると思います.

* はmethodも存在することを意味します.つまり,aMfArrayであれば,a.shallowcopy()が使えます.

  • 生成系
Matft Numpy
*Matft.mfarray.shallowcopy *numpy.copy
*Matft.mfarray.deepcopy copy.deepcopy
Matft.mfarray.nums numpy.ones * N
Matft.mfarray.arange numpy.arange
Matft.mfarray.eye numpy.eye
Matft.mfarray.diag numpy.diag
Matft.mfarray.vstack numpy.vstack
Matft.mfarray.hstack numpy.hstack
Matft.mfarray.concatenate numpy.concatenate
  • 変換系
Matft Numpy
*Matft.mfarray.astype *numpy.astype
*Matft.mfarray.transpose *numpy.transpose
*Matft.mfarray.expand_dims *numpy.expand_dims
*Matft.mfarray.squeeze *numpy.squeeze
*Matft.mfarray.broadcast_to *numpy.broadcast_to
*Matft.mfarray.conv_order *numpy.ascontiguousarray
*Matft.mfarray.flatten *numpy.flatten
*Matft.mfarray.flip *numpy.flip
*Matft.mfarray.swapaxes *numpy.swapaxes
*Matft.mfarray.moveaxis *numpy.moveaxis
*Matft.mfarray.sort *numpy.sort
*Matft.mfarray.argsort *numpy.argsort
  • ファイル関係 saveが未完成です.
Matft Numpy
Matft.mfarray.file.loadtxt numpy.loadtxt
Matft.mfarray.file.genfromtxt numpy.genfromtxt
  • 演算系

2行目が演算子です.

Matft Numpy
Matft.mfarray.add
+
numpy.add
+
Matft.mfarray.sub
-
numpy.sub
-
Matft.mfarray.div
/
numpy.div
.
Matft.mfarray.mul
*
numpy.multiply
*
Matft.mfarray.inner
*+
numpy.inner
n/a
Matft.mfarray.cross
*^
numpy.cross
n/a
Matft.mfarray.matmul
*&   
numpy.matmul
@ 
Matft.mfarray.equal
===
numpy.equal
==
Matft.mfarray.allEqual
==
numpy.array_equal
n/a
Matft.mfarray.neg
-
numpy.negative
-
  • 初等関数系
Matft Numpy
Matft.mfarray.math.sin numpy.sin
Matft.mfarray.math.asin numpy.asin
Matft.mfarray.math.sinh numpy.sinh
Matft.mfarray.math.asinh numpy.asinh
Matft.mfarray.math.sin numpy.cos
Matft.mfarray.math.acos numpy.acos
Matft.mfarray.math.cosh numpy.cosh
Matft.mfarray.math.acosh numpy.acosh
Matft.mfarray.math.tan numpy.tan
Matft.mfarray.math.atan numpy.atan
Matft.mfarray.math.tanh numpy.tanh
Matft.mfarray.math.atanh numpy.atanh

面倒なので,省略します...笑
ここを見てください

  • 高階関数系
Matft Numpy
*Matft.mfarray.stats.mean *numpy.mean
*Matft.mfarray.stats.max *numpy.max
*Matft.mfarray.stats.argmax *numpy.argmax
*Matft.mfarray.stats.min *numpy.min
*Matft.mfarray.stats.argmin *numpy.argmin
*Matft.mfarray.stats.sum *numpy.sum
  • 線形代数系
Matft Numpy
Matft.mfarray.linalg.solve numpy.linalg.solve
Matft.mfarray.linalg.inv numpy.linalg.inv
Matft.mfarray.linalg.det numpy.linalg.det
Matft.mfarray.linalg.eigen numpy.linalg.eig
Matft.mfarray.linalg.svd numpy.linalg.svd
Matft.mfarray.linalg.polar_left scipy.linalg.polar
Matft.mfarray.linalg.polar_right scipy.linalg.polar

インストール

SwiftPMとCocoaPodに対応しました.

SwiftPM

  • Import
    • Project > Build Setting > + Build Setting
    • 適宜選択 select
  • アップデート
    • File >Swift Packages >Update to Latest Package versions update

CocoaPods

  • Podfile作成 (すでにある場合は無視)
  pod init
  • pod 'Matft'をPodfileに追記
  target 'your project' do
    pod 'Matft'
  end
  • インストール
  pod install

Performance

Accelerateに任せているので,計算は担保されていると言いましたが,足し算だけ速度を計算してみました.
時間があれば他の関数も調べます...

case Matft Numpy
1 1.14ms 962 µs
2 4.20ms 5.68 ms
3 4.17ms 3.92 ms
  • Matft
func testPefAdd1() {
        do{
            let a = Matft.mfarray.arange(start: 0, to: 10*10*10*10*10*10, by: 1, shape: [10,10,10,10,10,10])
            let b = Matft.mfarray.arange(start: 0, to: -10*10*10*10*10*10, by: -1, shape: [10,10,10,10,10,10])

            self.measure {
                let _ = a+b
            }
            /*
             '-[MatftTests.ArithmeticPefTests testPefAdd1]' measured [Time, seconds] average: 0.001, relative standard deviation: 23.418%, values: [0.001707, 0.001141, 0.000999, 0.000969, 0.001029, 0.000979, 0.001031, 0.000986, 0.000963, 0.001631]
            1.14ms
             */
        }
    }

    func testPefAdd2(){
        do{
            let a = Matft.mfarray.arange(start: 0, to: 10*10*10*10*10*10, by: 1, shape: [10,10,10,10,10,10])
            let b = a.transpose(axes: [0,3,4,2,1,5])
            let c = a.T

            self.measure {
                let _ = b+c
            }
            /*
             '-[MatftTests.ArithmeticPefTests testPefAdd2]' measured [Time, seconds] average: 0.004, relative standard deviation: 5.842%, values: [0.004680, 0.003993, 0.004159, 0.004564, 0.003955, 0.004200, 0.003998, 0.004317, 0.003919, 0.004248]
            4.20ms
             */
        }
    }

    func testPefAdd3(){
        do{
            let a = Matft.mfarray.arange(start: 0, to: 10*10*10*10*10*10, by: 1, shape: [10,10,10,10,10,10])
            let b = a.transpose(axes: [1,2,3,4,5,0])
            let c = a.T

            self.measure {
                let _ = b+c
            }
            /*
             '-[MatftTests.ArithmeticPefTests testPefAdd3]' measured [Time, seconds] average: 0.004, relative standard deviation: 16.815%, values: [0.004906, 0.003785, 0.003702, 0.005981, 0.004261, 0.003665, 0.004083, 0.003654, 0.003836, 0.003874]
            4.17ms
             */
        }
}
  • Numpy
In [1]:
import numpy as np
#import timeit

a = np.arange(10**6).reshape((10,10,10,10,10,10))
b = np.arange(0, -10**6, -1).reshape((10,10,10,10,10,10))

#timeit.timeit("b+c", repeat=10, globals=globals())
%timeit -n 10 a+b
962 µs ± 273 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [2]:
a = np.arange(10**6).reshape((10,10,10,10,10,10))
b = a.transpose((0,3,4,2,1,5))
c = a.T
#timeit.timeit("b+c", repeat=10, globals=globals())
%timeit -n 10 b+c
5.68 ms ± 1.45 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [3]:
a = np.arange(10**6).reshape((10,10,10,10,10,10))
b = a.transpose((1,2,3,4,5,0))
c = a.T
#timeit.timeit("b+c", repeat=10, globals=globals())
%timeit -n 10 b+c
3.92 ms ± 897 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

個人的に気に入っているところ

  • 名前
  • 複数型対応
  • Numpyぽさ
  • Indexing
  • Row Major,Column Majorに対応した点(ここ
  • Accelerateに渡すアルゴリズム(ここ

微妙なところ

  • まだまだオーバーヘッドが存在する
    • せっかくMfArrayのOrderを保持するflagを実装したのに,無駄なコピーをしている箇所がいくつかある
    • 生成系がArrayMfArrayに変換している(vDSPとかを使いたい)
  • Protocolがうまく使えていない

最後に

気まぐれで作成しましたが,思ったよりいい感じのものができました.ただただ僕の検索力不足なだけで,より良いライブラリがあるかもしれませんが,是非試していただけると嬉しいです.(環境依存のチェックができていませんので,是非お願いします...)
とにもかくにもいい勉強になりました.ありがとうございました.

参考

numpy
scipy
Accelerate
SwiftでNDArray書く(テストケースを参考にさせていただきました.)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOSの購読管理画面の呼び出し方

メモな感じですいません。
iOSの購読管理画面を呼び出す方法、ここに書いてあります。
https://developer.apple.com/jp/documentation/storekit/in-app_purchase/

let url = "https://apps.apple.com/account/subscriptions"       
UIApplication.shared.open(URL(string: url)!, options: [:], completionHandler: nil)

WWDCのビデオはこちらです。
https://developer.apple.com/videos/play/wwdc2018/705/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SF SymbolsをiOS12でも使用できるようにする

SF Symbolsとは

Apple公式のアイコンフォントです
image.png

しかしiOS13から使えない?
image.png

iOS12でも使えるように

環境

Xcode 11.4.1

準備

SF Symbolsアプリをダウンロードする
https://developer.apple.com/design/human-interface-guidelines/sf-symbols/overview/

スクリーンショット 2020-05-15 13.52.12.png

SVGで出力する
スクリーンショット 2020-05-15 13.54.15.png

XcodeのAssets内で『+』を押して『New Symbol Image Asset』選択して
image.png

SF Symbolsアプリから出力したSVGファイルをインポートすることで準備完了
image.png

あとはStoryBoard、コードでセットするだけで使用できます

使用する

Sample.swift
let image = UIImage(name:"Pin")
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】アルバム起動

①photosをインポートする

import Photos

② UIImagePickerControllerDelegate,UINavigationControllerDelegate を継承する。

UIImagePickerControllerDelegate,UINavigationControllerDelegate

③以下のコードをメソッドとして記述する。

//アルバムを起動
    func doAlbum(){
        let sourceType:UIImagePickerController.SourceType = .photoLibrary
        //カメラが利用可能かチェックする
        if UIImagePickerController.isSourceTypeAvailable(.photoLibrary){
            let cameraPicker = UIImagePickerController()
            cameraPicker.sourceType = sourceType
            cameraPicker.delegate = self
            cameraPicker.allowsEditing = true
            present(cameraPicker, animated: true, completion: nil)
        }
    }

④アルバムから画像が選択されたときの呼ばれる箇所。以下のコードを記述する。

//カメラ撮影orアルバムから画像選択された時に呼ばれる
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {

    if info[.originalImage] as? UIImage != nil{
        let selectedImage = info[.originalImage] as! UIImage
        UserDefaults.standard.set(selectedImage.jpegData(compressionQuality: 0.1), forKey: "userImage")
        logoImageView.image = selectedImage
        picker.dismiss(animated: true, completion: nil)

    }
}

※理解のため、自分のわかりやすい単語に置き換えてあるので、表記に問題ありの場合が多いかと思いますが、ご了承ください。。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

NSView alphaValueが効かない時に

NSView.alphaValueを設定するとViewが全く見えなくなるバグに遭遇したので、解決方の備忘録。

これを半透明したいのに
スクリーンショット 1.png

完全に消えてしまう。
スクリーンショット.png

alphaValueは内部的にCALayerを使っているようで、場合によってはCALayerなしでもうまく行くが、場合によってはうまくいかないようなので

view.wantsLayer = true

で解決した。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】pinch in or out ができない時はisExclusiveTouchがtrueになっているかも!

pinch in or out ができない時はisExclusiveTouchがtrueになっているかも!

UIScrollViewのZoom周りをちゃんと設定したが、うまく拡大、縮小できないときはisExclusiveTouch = trueになっていないか確認しましょう。

UIView.appearance().isExclusiveTouch = true
UIButton.appearance().isExclusiveTouch = true
self.isExclusiveTouch = true

上記のような設定がUIScrollView上のViewやCellに設定されていると、複数のViewで同時にTouchイベントを処理しなくなります。
なので、pinch in, outのような複数の指で行う動作がうまく行かないときがあります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

swiftのシーケンスとコレクションを扱うためのプロトコル 超初心者のメモ

シーケンスとコレクションを扱うためのプロトコル

シーケンスとはその要素に一方向から順次アクセス可能なデータ構造です。
例えば、配列は先頭のインデックスから要素に順次アクセスできるため、シーケンスの一種であると言えます。
標準ライブラリにはシーケンスを汎用的に扱うためにSequenceプロトコルが用意されています。

コレクションは、一方向からの順次アクセスと、特定のインデックスの値への直接アクセスが可能なデータ構造です。
機能の差からわかるように、コレクションはシーケンスを内包する概念です。

Sequenceプロトコルは、次のインターフェースを提供します。
forEach
filter
map
flatMap
compactMap
reduce

使い方の例

filter
指定した条件を満たす要素のみを含む、新しいシーケンスを返すメソッドです。

let array  = [1, 2, 3, 4, 5]
let enumerated2 = array.filter({e in e % 2 == 0})
print(enumerated2) // 2, 4

compactMap
compactMapは全ての要素を特定の処理で変換するという点ではmapメソッドと同じですが、変換できない値を無視するという点が異なります。

let strings = ["foo", "123", "bar", "456"]
let integers = strings.compactMap({value in Int(value)})
print(integers)

結果は、変換に成功した123と456のみを含むInt型の配列です。変換に失敗した"foo"や"bar"は無視された事がわかります。

Collectionプロトコルは、Sequenceプロトコルを継承しています。
そのため、Collectionプロトコルにい準拠する型に対しては、Sequenceプロトコルが提供するfilterや、mapを使用可能です。

let array = [1, 2, 3, 4, 5]
array[3] // 4
array.isEmpty // false
array.count // 5
array.first // 1
array.last // 5
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UIalertControllerが微妙に使いづらかったのでいい感じのAlertControllerを自作する!

はじめに

先日インターン先のタスクで共通で使う通知部分を実装する際、デフォルトのUIAlertControllerだとボタンのテキストをいじれなかったりとデザインに合わせることが難しかったので、一からUialertControllerに似た実装の仕方で作ってみたのでそれについてまとめてみようと思います!

github

https://github.com/ikemoti/OriginalUIAlertController

デザインの用件内容

・アラートのttitleやmessage部分のフォントやtextColorを変えたい
(デフォルトのアラートだと変更不可能)
・アラートの上部にバーナー画像を任意で追加可能にして欲しい
・ボタンの数を任意で選択できるようにしてフォントや文字色も選択可能にして欲しい
・なるべく使う際にUIAlerttControllerに似た使い方ができるようにして欲しい

完成図

①アニメーション部分

はじめに土台となるアラートのアニメーション部分を作成しました!

a
public class DialogAnimation: NSObject, UIViewControllerAnimatedTransitioning {
    // true: dismiss
    // false: present
    private let isPresenting: Bool
    init(isPresenting: Bool) {
        self.isPresenting = isPresenting
    }
    public func transitionDuration(using _: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.20
    }
    public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        if isPresenting {
            dismissAnimation(transitionContext)
        } else {
            presentAnimation(transitionContext)
        }
    }
    private func presentAnimation(_ transitionContext: UIViewControllerContextTransitioning) {
        guard let alert = transitionContext
            .viewController(forKey: UITransitionContextViewControllerKey.to) as? DialogController
            else { fatalError("ViewController is not defined sucessfully") }
        let container = transitionContext.containerView
        alert.backgroundView.alpha = 0
        alert.contentView.alpha = 0
        alert.contentView.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
        container.addSubview(alert.view)
        UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
            alert.backgroundView.alpha = 1
            alert.contentView.alpha = 1
            alert.contentView.transform = .identity
        }, completion: { _ in
            transitionContext.completeTransition(true)
        })
    }
//DissMissAnimaitonb部分は省略

ポイントとして
ただ単にViewを表示するだけだと,遷移前の画面が投下されないので表示されるViewをbackgroundViewとAlertViewに分割してbackgroundViewを透過させることで無事表示できるようになりました

②Controller部分について

a
//Controller部分
public class DialogController: UIViewController, UIViewControllerTransitioningDelegate {
    //DialogControllerのベースとなるView
    private(set) lazy var backgroundView: UIView = {
        let view = UIView()
        view.backgroundColor = UIColor.black.withAlphaComponent(0.15)
        view.isOpaque = false
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    let contentView: UIView = .init()
    let contentStackView: UIStackView = .init()

    private let bannerView: UIImageView = .init()
    private var bannerAspectRatioConstraint: NSLayoutConstraint?
    private let textContainer: UIView = UIView()
    private let textStackView: UIStackView = UIStackView()
    private lazy var textStackViewTopAnchor: NSLayoutConstraint = {
        return textStackView.topAnchor.constraint(equalTo: textContainer.topAnchor, constant: 40)
    }()
    private lazy var textStackViewBottomAnchor: NSLayoutConstraint = {
        return textStackView.bottomAnchor.constraint(equalTo: textContainer.bottomAnchor, constant: -40)
    }()
    private let titleLabel: UILabel = .init()
    private let messageLabel: UILabel = .init()
    private let buttonStackview: UIStackView = .init()
    private let topBorder: UIView = .init()
    private var actions = [DialogAction]()

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        providesPresentationContextTransitionStyle = true
        definesPresentationContext = true
        modalPresentationStyle = UIModalPresentationStyle.custom
        transitioningDelegate = self
    }
    //簡易イニシャライザ
    //引数がnilかどうかでcontentViewが可変する
    public convenience init(title: String, message: String?, banner: UIImage?) {
        self.init(nibName: nil, bundle: nil)
        titleLabel.text = title
        messageLabel.isHidden = message == nil
        messageLabel.text = message
        bannerView.image = banner
        bannerView.isHidden = banner == nil
        textStackViewTopAnchor.constant = banner == nil ? 40 : 24
        textStackViewBottomAnchor.constant = banner == nil ? -40 : -24

        if let banner = banner {
            bannerAspectRatioConstraint
                = bannerView.heightAnchor.constraint(equalTo: bannerView.widthAnchor,
                                                     multiplier: banner.size.height / banner.size.width)
        }
    }

    required init?(coder _: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    public override func viewDidLoad() {
        super.viewDidLoad()
        setAttrributes()
        constructViews()
        setLayout()
    }

    @objc private func buttonEvent(sender: UIButton) {
        self.dismiss(animated: true, completion: nil)
    }

    public func animationController(forDismissed _: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return DialogAnimation(isPresenting: true)
    }
//ボタン追加時のメゾット、二つ目のボタン追加時からボタンの追加前にborderを設置する
    public func add(action: DialogAction) {
        let button = UIButton()
        button.setTitle(action.title, for: UIControl.State())
        button.setTitleColor(action.titleColor, for: .normal)
        button.titleLabel?.font = action.titleFont
        button.heightAnchor.constraint(equalToConstant: 44).isActive = true
        button.addTarget(self, action: #selector(buttonEvent(sender:)), for: UIControl.Event.touchUpInside)
        button.translatesAutoresizingMaskIntoConstraints = false
        if actions.count > 0 {
            let border = UIView()
            border.translatesAutoresizingMaskIntoConstraints = false
            border.widthAnchor.constraint(equalToConstant: 0.5).isActive = true
            border.heightAnchor.constraint(equalToConstant: 44).isActive = true
            border.backgroundColor = UIColor.gray
            buttonStackview.addArrangedSubview(border)
        }
        buttonStackview.addArrangedSubview(button)
        if let alertView = buttonStackview.arrangedSubviews.first {
            button.widthAnchor.constraint(equalTo: alertView.widthAnchor ).isActive = true
        }
        actions.append(action)
    }

    public func animationController(
        forPresented _: UIViewController,
        presenting _: UIViewController,
        source _: UIViewController
    ) -> UIViewControllerAnimatedTransitioning? {
        return DialogAnimation(isPresenting: false)
    }
}

Controller部分に関して引数の有無に合わせてのcontentViewの変化するようにしたり、
ボタンの追加時に個数によってボーダーを追加するような実装にしました!

③ボタンに関して

a
public class DialogAction {
    public enum ActionStyle {
        case `default`
        case cancel
        case destructive
        case custom(color: UIColor, font: UIFont)
    }
    private var handler: ((_ action: DialogAction) -> Void)?
    public private(set) var style: ActionStyle
    public private(set) var title: String
    public var titleColor: UIColor {
        switch self.style {
            case .default: return UIColor.black
        case .cancel: return UIColor.black
            case .destructive: return UIColor.red
            case let .custom(color, _): return color
        }
    }
    public var titleFont: UIFont {
        switch self.style {
            case .default: return UIFont.boldSystemFont(ofSize: 14)
            case .cancel: return UIFont.systemFont(ofSize: 14)
        case .destructive: return UIFont.boldSystemFont(ofSize: 14)
            case let .custom(_, font): return font
        }
    }
    public init(title: String, style: ActionStyle, handler: ((_ action: DialogAction) -> Void)?) {
        self.handler = handler
        self.style = style
        self.title = title
    }
}

ボタンに関しては通常はdefault、cancel、destructiveの三つですが今回自由にカスタムできるように作って欲しいとのことだったのでcustomを追加してフォントとテキストカラーを自由に選べるようにしました!

④完成時の実装例

a
class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()}

    @IBAction func `default`(_ sender: Any) {
        let alert = UIAlertController(title: "タイトル", message: "メッセージ", preferredStyle: .alert)
        let cancel = UIAlertAction(title: "キャンセル", style: .cancel,handler: nil)
        let ok = UIAlertAction(title: "OK", style: .default,handler: nil)
        alert.addAction(cancel)
        alert.addAction(ok)
        self.present(alert, animated: true, completion: nil)
    }


    @IBAction func Original(_ sender: Any) {
        let alert2 = DialogController(title: "タイトル", message:"メッセージ", banner:UIImage(imageLiteralResourceName:"Image" ) )
        let cancel2 = DialogAction(title: "キャンセル", style: .custom(color: UIColor.purple, font: UIFont.systemFont(ofSize: 16)), handler:nil )
        let ok2 = DialogAction(title: "Ok", style: .default,handler: nil)
        alert2.add(action: cancel2)
        alert2.add(action: ok2)
        self.present(alert2, animated: true, completion: nil)
    }

今回無事に実装できたのですが、animation関連がの知識不足を感じたので、また勉強していきたいなと思いました!?‍♂️

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

こんなソースコードはイヤだ-どうしてもindexが欲しいのか

プログラムのソースコードを書く時のアンチパターンをまとめていこうと思います。

どうしてもindexが欲しいのか

sample.swift
  let fruits = ["apple", "lemon", "melon"]
  let colors = ["red", "yellow", "green"]
  for i in 0..<fruits.count {
    print(colors[i])
  }

こんなソースコードはイヤだの解説(どこがいけないの?)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

アニメ画像の昼/夜認識システムの作成:(1/3)「Create ML」を用いた機械学習モデルを作成

本シリーズの記事一覧:

意図:

  1. Mac OSの壁紙として、時刻に合わせてさまざまなアニメ画像を設定するアプリの開発。

  2. 機械学習を使用して、アニメ画像に自動的に昼または夜としてのマーク付けを行う。

アニメ画像から昼夜の状態を認識する必要があるため、機械学習を取り入れます。

このアプリは、例えば日中には昼の場面のさまざまなアニメ画像を壁紙として設定します。そして夜間には、夜の場面のさまざまなアニメ画像を壁紙に設定します。

こちらは、「Create ML」を用いて機械学習モデルを作成する方法を解説する記事の第1部となります。

早速始めましょう!

機械学習モデルのトレーニング

まずは機械学習モデルのトレーニングと機械学習モデルファイルの取得に取り掛かりましょう。

「Create ML」ツールを開く

「Xcode」に搭載されている「Create ML」は、「iOS」、「iPadOS」、および「Mac OS」で使える機械学習モデル(拡張子.mlmodelのファイル)の生成に利用できる便利なツールで、「Xcode」内にインストールされています。

開くには、画面上部のシステムバーにある「Xcode」をクリックします。

Screen Shot 2020-05-12 at 11.02.25 PM.png

そして、表示されたメニューから、「Open Developer Tool(開発者ツールを開く)」を選択し、「Create ML」をクリックして開きます

Screen Shot 2020-05-12 at 11.04.58 PM.png

開いた「Create ML」ツールの画面上部のシステムバーにある「File(ファイル)」をクリックし、その後「New Project(新規プロジェクト)」をクリックします

Screen Shot 2020-05-12 at 11.05.55 PM.png

「Image Classifier(画像分類システム)」を選択します。画像を入力するとその画像のラベルを出力するモデルをトレーニングするということです。

スクリーンショット 2020-05-12 午後11.30.27.png

次に、情報を入力して「Next(次)」をクリックします

「Create(作成)」をクリックしてモデルを作成します。

画像の収集

ここでは、プログラムに任意のアニメ画像の時間帯が昼か夜かを予測させることを目指しています。この機械学習モデルをトレーニングするには、2グループの画像、つまり昼の時間帯の画像と暗い(夜の)時間帯の画像を見つける必要があります。

そこで、「アニメ」という名前のフォルダーを作ります。そして、「アニメ」の中に2つのフォルダーを作り、名前は「昼」と「夜」にします。「アニメ」フォルダーの中にはそのほかには何も入れないでください。インターネットから何枚かアニメ画像を取得し、時間帯を判断して「昼」フォルダーか「夜」フォルダーにドラッグします。

スクリーンショット 2020-05-13 午後3.50.33.png

用意する画像数が多ければ多いほど、モデルはより正確になります。

モデルのトレーニングの開始

次に、「Create ML」アプリに切り替えて、「Training Data(トレーニングデータ)」セクションを見つけ、「Choose(選択)」ドロップダウンメニューをクリックして「Select Files(ファイルを選択)」をクリックします

スクリーンショット 2020-05-13 午後3.52.17.png

先ほど作成したフォルダーを選択して「Open(開く)」をクリックします

スクリーンショット 2020-05-13 午後3.58.13.png

すると、黄色の矢印で、「Classes(クラス)」の総数、つまりカテゴリー数が指し示されて表示されます。青の矢印で用意した画像の総数が指し示されて表示されます。

「Augmentations(拡張)」の設定

スクリーンショット 2020-05-13 午後4.07.36.png

実行ボタンをクリックしてトレーニングを開始します:

スクリーンショット 2020-05-13 午後4.05.30.png

処理中

お茶でも楽しみながらトレーニングの完了を待ちます。

スクリーンショット 2020-05-13 午後4.06.24.png

トレーニング完了

スクリーンショット 2020-05-13 午後4.07.08.png

テスト用画像を何枚か用意してモデルをテストします

モデルのトレーニングが完了したので、どれほど正確なモデルになったかをテストしましょう。Testing(テスト)セクションに切り替えましょう。

スクリーンショット 2020-05-13 午後4.22.19.png

次に、もう1つフォルダーを作り、名前を「Testing(テスト)」にします。その中に2つのフォルダーを作り、名前を「Day(昼)」と「Night(夜)」にします。新たにいくつかアニメの画像を取得して、それぞれのフォルダーに分類して保存します。

先ほどと同じように、「Choose(選択)」、「Select Files(ファイルを選択)」をクリックします。そしてこのフォルダーを選択します。

「Test Model(モデルをテスト)」をクリックします

スクリーンショット 2020-05-13 午後4.26.32.png

この例では正確性は100%となりました!素晴らしい結果です!

スクリーンショット 2020-05-13 午後4.27.57.png

お手元では正確性が低くても、心配することはありません。画像をさらに追加して再度トレーニングを開始してください。

直接テスト

画像をドラッグ・ドロップするだけでテストすることもできます。

「Output(出力)」をクリックし、左のパネルに画像をドラッグするだけで結果を確かめられます。

スクリーンショット 2020-05-14 午後3.54.21.png

モデルの書き出し

「Output(出力)」にあるモデルファイルを見つけ、フォルダーにドラッグするだけです。

スクリーンショット 2020-05-13 午後4.34.12.png

次の記事の内容

次の記事では、このモデルをコードから呼び出すことで、どのようにアニメ画像の時間帯の昼/夜の認識ができるかを解説します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む