[PYTHON] Utilisez numpy.ndarray pour l'évaluation de plusieurs longueurs

Préface

Dans le système python3, les entiers peuvent être traités comme des chiffres infinis par défaut, et si le package mpmath est utilisé, plusieurs opérations numériques de longueur de nombres réels (nombres complexes) sont possibles. Personnellement, je l'utilise en combinaison avec numpy.array, mais je dois faire attention, je vais donc enregistrer les points à noter dans un mémorandum personnel.

Démo d'évaluation de plusieurs longueurs

>>> import python 
>>> math.factorial(100)                                                                                     
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
>>> import mpmath
>>> mpmath.mp.dps = 100
>>> print(mpmath.pi)                                                                                            
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068

Confirmation de l'exactitude

Vous pouvez vérifier la précision du float standard python avec la commande suivante

>>> import sys 
>>> sys.float_info                                                          
sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308,
			   min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, 
			   dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1)

epsilon est [Computer Epsilon](https://ja.wikipedia.org/wiki/%E8%A8%88%E7%AE%97%E6%A9%9F%E3%82%A4%E3%83%97% E3% 82% B7% E3% 83% AD% E3% 83% B3), qui est le plus petit nombre à côté de 1. J'ai lu que le [array (ndarray)] de numpy (https://docs.scipy.org/doc/numpy/reference/arrays.html) est implémenté en langage C, donc j'ai lu que c'est un type de langage C. Soyez contraint. En fait, dtype est spécifié lors de la création de numpy.array.

Dans le cas de numpy, il peut être confirmé par finfo (virgule flottante) et iinfo (entier) [numpy: overflow-error](https://docs.scipy.org/doc/numpy/user/basics.types.html#overflow -les erreurs)

>>> import numpy as np                                                          
>>> np.finfo(np.float)                                                             
finfo(resolution=1e-15, min=-1.7976931348623157e+308, max=1.7976931348623157e+308, dtype=float64)
>>> np.finfo(np.float128)                                                                                                                                                             
finfo(resolution=1e-18, min=-1.189731495357231765e+4932, max=1.189731495357231765e+4932, dtype=float128)
>>> np.iinfo(np.int)                                                                                                
iinfo(min=-9223372036854775808, max=9223372036854775807, dtype=int64)

Vous pouvez vérifier à partir de. float128 est un long double en C, et non float128 (4x virgule flottante). Le comportement de Overflow

>>> np.array([np.finfo(np.float).max], dtype=np.float) * 2  
array([inf])
>>> np.array([np.iinfo(np.int).max], dtype=np.int) + 1                                                              
array([-9223372036854775808])

Il devient. La virgule flottante a également une double barrière de précision en python, il semble donc inévitable de déborder par rapport à float. Cependant, comme python3 peut gérer int avec une précision infinie, il peut provoquer par inadvertance un débordement ou un manque de précision involontaire. Si vous souhaitez que numpy.array gère les entiers qui dépassent la limite de précision de np.int, vous pouvez laisser dtype non spécifié ou spécifier object pour dtype.

En d'autres termes

import math
>>> np.array([math.factorial(25)], dtype=np.int)                                                                    
Traceback (most recent call last):
  File "<ipython-input-46-0451d8765c1b>", line 1, in <module>
    np.array([math.factorial(25)], dtype=np.int)
OverflowError: Python int too large to convert to C long

Mais

>>> np.array([math.factorial(25)])                                                                                  
array([15511210043330985984000000], dtype=object)

Il devient. Si dtype est spécifié (non spécifié) pour object, plusieurs points flottants de longueur tels que mpmath peuvent être gérés.

Utilisez numpy.array avec plusieurs fractions flottantes multiples (mpmath)

Comme dans le cas de int, dtype doit être non spécifié (ou dtype = object).

>>> import mpmath                                                                                                   
>>> mpmath.mp.dps = 50                                                                                             
>>> np.array([mpmath.mpf("1")])/3                                                                                   
array([mpf('0.33333333333333333333333333333333333333333333333333311')], dtype=object)

Dans le passé, si vous changiez l'ordre des opérations, vous pourriez vous fâcher qu'il n'était pas défini, mais maintenant vous pouvez effectuer presque les mêmes opérations à quatre règles sans savoir si dtype est un objet, np.float ou np.int (tableau). Si les éléments de sont capables de quatre règles). Bien sûr, si vous effectuez une opération avec un tableau dont le dtype est np.float (ou np.int), la précision sera perdue.

>>> np.array(mpmath.linspace(0, 1, 10)) + np.linspace(0, 10, 10)                                                    
array([mpf('0.0'),
       mpf('1.2222222222222222715654677611180684632725185818142358'),
       mpf('2.4444444444444445431309355222361369265450371636284716'),
       mpf('3.6666666666666668146964032833542053898175557454427101'),
       mpf('4.8888888888888890862618710444722738530900743272569433'),
       mpf('6.1111111111111109137381289555277261469099256727430567'),
       mpf('7.3333333333333336293928065667084107796351114908854202'),
       mpf('8.555555555555556345047484177889095412360297309027773'),
       mpf('9.7777777777777781725237420889445477061801486545138865'),
       mpf('11.0')], dtype=object)

Comparaison de la vitesse de calcul

Dans numpy.array, dtype = np.int et np.float donneront les meilleures performances de vitesse de calcul. Vérifiez à quel point il est lent lorsque dtype = object.

import numpy as np
import mpmath
import time

def addsub(x, imax):
    x0 = np.copy(x)
    time0 = time.time()
    for n in range(imax):
        xx = x + x
        x = xx - x
    print(time.time()-time0, "[sec]", sum(x-x0), x.dtype)

def powpow(x, imax):
    x0 = np.copy(x)
    time0 = time.time()
    for n in range(imax):
        xx = x**(3)
        x = xx**(1/3)
    print(time.time()-time0, "[sec]", float(sum(x-x0)), x.dtype)

imax = 10000
sample = 10000

x = np.arange(0, sample, 1, dtype=np.int)
addsub(x, imax)

x = np.arange(0, sample, 1, dtype=object)
addsub(x, imax)


x = np.arange(0, sample, 1, dtype=np.float)
powpow(x, imax)

x = np.arange(0, sample, 1, dtype=object)
powpow(x, imax)

Vient ensuite le résultat de l'exécution

0.06765484809875488 [sec] 0 int64
3.387320041656494 [sec] 0 object
8.931994915008545 [sec] -0.00024138918300131706 float64
16.408015489578247 [sec] -0.00024138918300131706 object

Lorsque l'élément de array est int, il a tendance à être extrêmement lent lorsqu'il est spécifié en tant qu'objet, mais d'un autre côté, float est environ deux fois plus élevé que la plage autorisée. Considérons maintenant le cas de mpmath. Puisque la partie formelle par défaut de mpmath est de 15 chiffres, comparons la vitesse de calcul entre 15 chiffres et 100 chiffres de la partie formelle. Cependant, avec les paramètres ci-dessus, cela prendra plusieurs heures, donc imax sera supprimé de deux chiffres. Par conséquent, si vous multipliez le temps de calcul par 100 pour comparer avec le résultat ci-dessus, vous pouvez comparer approximativement.

imax = 100

mpmath.mp.dps=100
x = np.array(mpmath.arange(0, sample, 1), dtype=object)
powpow(x, imax)

Vient ensuite le résultat de l'exécution

15.098075151443481 [sec] -2.4139151442170714e-06 object

Comme la quantité de calcul est réduite à 1/100 avec mpmath et 15 chiffres de la partie formelle, cela prend environ 100 fois.

À propos, la définition de powpow n'améliore pas la précision. Parce que lorsque $ x ^ {1/3} $ est défini, la partie exposant est évaluée par le flottant de python, donc les nombres après 15 chiffres n'ont pas de sens. Alors j'ai un peu modifié powpow

def papawpowpow(x, imax):
    x0 = np.copy(x)
    time0 = time.time()
    for n in range(imax):
        xx = x**(3)
        x = xx**(mpmath.mpf("1/3"))
    print(time.time()-time0, "[sec]", float(sum(x-x0)), x.dtype)

Lorsqu'il est exécuté comme

21.777454376220703 [sec] 1.5530282978129947e-91 object
mpmath.mp.dps=100
x = np.array(mpmath.arange(0, sample, 1), dtype=object)
papawpowpow(x, imax)

Au fait, si vous évaluez avec élément par élément sans utiliser de tableau

def elewisepapawpowpow(x, imax):
    x0 = x[:]
    time0 = time.time()        
    for n in range(imax):
        xx = [x1**(3) for x1 in x]
        x =  [x1**(mpmath.mpf("1/3")) for x1 in xx]
    diff = [x[i] - x0[i] for i in range(len(x0))]
    print(time.time()-time0, "[sec]", float(sum(diff)), type(x))            

Il devient.

28.9152569770813 [sec] 1.5530282978129947e-91 <class 'list'>

La vitesse de calcul est un peu moins de 30% plus rapide que dans le cas de numpy.array, et il est plus propre d'utiliser numpy.array, qui est familier avec le codage.

Environnement d'exécution

$ lscpu 
architecture:                      x86_64
Mode de fonctionnement du processeur:                      32-bit, 64-bit
Ordre des octets:                          Little Endian
CPU:                                 4
Liste des processeurs en ligne: 0-3
Nombre de threads par cœur:              1
Nombre de cœurs par socket:              4
Nombre de prises:                          1
Nombre de nœuds NUMA:                       1
Fournisseur ID:                         AuthenticAMD
Famille de CPU:                      23
modèle:                              17
Nom du modèle:                            AMD Ryzen 3 2200G with Radeon Vega Graphics
marcher:                        0
CPU MHz:                             1479.915
CPU maximum MHz:                        3500.0000
CPU minimum MHz:                        1600.0000
BogoMIPS:                            7000.24
Virtualisation:                              AMD-V
Cache L1d:                      32K
Cache L1i:                      64K
Cache L2:                       512K
Cache L3:                       4096K
Processeur NUMA nœud 0:                   0-3
drapeau:                              fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good nopl nonstop_tsc cpuid extd_apicid aperfmperf pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx f16c rdrand lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_llc mwaitx cpb hw_pstate sme ssbd sev ibpb vmmcall fsgsbase bmi1 avx2 smep bmi2 rdseed adx smap clflushopt sha_ni xsaveopt xsavec xgetbv1 xsaves clzero irperf xsaveerptr arat npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif overflow_recov succor smca
$ cat /proc/cpuinfo | grep MHz
cpu MHz		: 1624.805
cpu MHz		: 1500.378
cpu MHz		: 3700.129
cpu MHz		: 2006.406

Recommended Posts

Utilisez numpy.ndarray pour l'évaluation de plusieurs longueurs
EP 26 Utiliser l'héritage multiple uniquement pour les classes utilitaires mixtes
Utiliser PySide pour l'interface utilisateur HDA