J'ai créé une bibliothèque d'opérations matricielles à N dimensions Matft avec Swift

introduction

Ceci est le premier message. Comme le titre l'indique, j'ai créé une bibliothèque d'opérations matricielles à N dimensions ** Matft (https://github.com/jjjkkjjj/Matft) ** avec Swift. Vu. (** Opération Mat ** rix, Swi ** ft **, qui signifie Matft lol)

Le début de l'affaire était un mot d'un senior de l'entreprise, "Je veux que vous écriviez un code pour trouver la matrice inverse de 3 lignes et 3 colonnes en Swift." Je pensais que Swift aurait une bibliothèque d'opérations matricielles à N dimensions comme Numpy en Python, mais quand je l'ai recherchée, ce n'était pas surprenant. .. .. L'accélération officielle semble être gênante, et le fameux surtension fait jusqu'à 2 dimensions? C'était comme. C'est pourquoi j'ai décidé de créer ma propre bibliothèque d'opérations matricielles à N dimensions. (C'est complètement sur-spec pour le code qui trouve la matrice inverse de 3 lignes et 3 colonnes, mais lol)

De plus, Matft a été créé comme ça. Et comme c'est un gros problème, je vais le partager, et je suis à la hauteur du présent.

Aperçu

Fondamentalement, il a été créé selon Numpy de Python, de sorte que les noms et l'utilisation des fonctions sont presque les mêmes que Numpy.

Déclaration

La déclaration crée un tableau multidimensionnel avec «MfArray», qui est «ndarray».

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

Moule

Je voulais le rendre compatible avec différents types, alors j'ai préparé un certain type. Ce n'est pas «dtype» mais «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)

La liste de types est définie par le type Enum suivant.

 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 Des tranches comme ʻa [:, :: -1] dans Numpy sont également implémentées avec ~ <. (Au départ, il était implémenté avec ~, mais ~ 2` etc. Nous avons également implémenté des indices négatifs tels que -1. (Cela a peut-être été le plus difficile ...)

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]) // print(a[~<<-1])Avec un alias~<<Est également équivalent
/*
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]*/

Liste des autres fonctions

Voici une liste de fonctions spécifiques. Puisque le calcul principal est laissé à Accélérer, je pense que le temps de calcul est garanti dans une certaine mesure.

\ * Signifie que cette méthode existe également. En d'autres termes, si ʻa est MfArray, vous pouvez utiliser ʻa.shallowcopy () .

--Système de génération

Matft Numpy
*Matft.shallowcopy *numpy.copy
*Matft.deepcopy copy.deepcopy
Matft.nums numpy.ones * 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

--Système de conversion

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.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

--Lié aux fichiers la sauvegarde est incomplète.

Matft Numpy
Matft.file.loadtxt numpy.loadtxt
Matft.file.genfromtxt numpy.genfromtxt

--Système de calcul

La deuxième ligne est l'opérateur.

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.equal
===
numpy.equal
==
Matft.not_equal
!==
numpy.not_equal
!=
Matft.allEqual
==
numpy.array_equal
n/a
Matft.neg
-
numpy.negative
-
Matft Numpy
Matft.math.sin numpy.sin
Matft.math.asin numpy.asin
Matft.math.sinh numpy.sinh
Matft.math.asinh numpy.asinh
Matft.math.sin 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

Puisqu'il est gênant, je vais l'omettre. .. .. Lol Voir ici

--Système statistique

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 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.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

Installation

Ajout du support pour SwiftPM, CocoaPod et Carthage.

SwiftPM

CocoaPods

--Créer Podfile (ignorer s'il existe déjà)

  pod init
  target 'your project' do
    pod 'Matft'
  end

--Installation

  pod install

Carthage (non confirmé)

-Créer dans Cartfile et lire.

echo 'github "realm/realm-cocoa"' > Cartfile
carthage update ###or append '--platform ios'

――C'est OK si vous chargez le Matft.framework terminé dans le projet.

Performance

J'ai dit que le calcul est garanti car je le laisse à Accélérer, mais j'ai calculé la vitesse uniquement en ajoutant. Si j'ai le temps, j'étudierai d'autres fonctions. .. ..

case Matft Numpy
1 1.14ms 962 µs
2 4.20ms 5.68 ms
3 4.17ms 3.92 ms
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.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.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
             */
        }
}
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)

Ce que j'aime personnellement

Subtilités

finalement

J'ai réussi sur un coup de tête, mais j'ai obtenu quelque chose de mieux que ce à quoi je m'attendais. Il existe peut-être une meilleure bibliothèque simplement à cause de mon manque de puissance de recherche, mais je vous serais reconnaissant de bien vouloir l'essayer. (La vérification dépendant de l'environnement n'est pas terminée, n'hésitez donc pas à nous contacter ...) Quoi qu'il en soit, c'était une bonne étude. Merci beaucoup.

référence

numpy scipy Accelerate Écrivez NDArray dans Swift (j'ai fait référence au scénario de test.)

Recommended Posts

J'ai créé une bibliothèque d'opérations matricielles à N dimensions Matft avec Swift
J'ai créé un installateur Ansible
J'ai essayé Smith en standardisant une matrice entière avec Numpy
J'ai fait un blackjack avec du python!
Essayez l'opération matricielle avec NumPy
J'ai créé un serveur Xubuntu.
J'ai fait une animation qui renvoie la pierre d'Othello avec POV-Ray
J'ai créé COVID19_simulator avec JupyterLab
J'ai créé Word2Vec avec Pytorch
J'ai fait un blackjack avec Python.
J'ai créé wordcloud avec Python.
J'ai créé une bibliothèque qui lit facilement les fichiers de configuration avec Python
Je souhaite utiliser une bibliothèque externe avec IBM Cloud Functions
[Python] J'ai créé une visionneuse d'images avec une fonction de tri simple.
J'ai fait un peintre discriminateur Anpanman
J'ai créé ma propre bibliothèque Python
J'ai fait une loterie avec Python.
J'ai envoyé un SMS avec Python
J'ai fait un kit de démarrage angulaire
J'ai créé un démon avec Python
J'ai créé la partie APL avec la compétence Alexa "Conversion des termes de l'industrie"
J'obtiens une erreur avec les pandas d'importation.
J'ai essayé d'envoyer un SMS avec Twilio
J'ai fait un compteur de caractères avec Python
J'ai créé une application d'analyse de fréquence en ligne
J'ai créé un module alternatif pour les japandas.
J'ai fait une carte hexadécimale avec Python
J'ai fait un jeu de vie avec Numpy
J'ai fait un générateur Hanko avec GAN
J'ai fait un jeu rogue-like avec Python
J'ai fait un simple blackjack avec Python
J'ai créé un fichier de configuration avec Python
J'ai fait une bibliothèque pour l'assurance actuarielle
J'ai fait une application WEB avec Django
J'ai fait un simulateur de neurones avec Python
Avec LINEBot, j'ai fait une application qui m'informe de "l'heure du bus"
J'ai fait une image pour qemu avec Yocto, mais j'ai échoué et j'ai recommencé