[PYTHON] Verwenden Sie numpy.ndarray für die Auswertung mehrerer Längen

Vorwort

Im Python3-System können Ganzzahlen standardmäßig als unendliche Ziffern behandelt werden. Wenn das mpmath-Paket verwendet wird, sind numerische Operationen mit mehreren Längen von reellen Zahlen (komplexen Zahlen) möglich. Persönlich verwende ich es in Kombination mit numpy.array, aber ich muss vorsichtig sein, damit ich die zu verzeichnenden Punkte als persönliches Memorandum aufzeichne.

Demo der Bewertung mehrerer Längen

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

Bestätigung der Richtigkeit

Sie können die Genauigkeit von Python Standard Float mit dem folgenden Befehl überprüfen

>>> 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 ist [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), die kleinste Zahl neben 1. Ich habe gelesen, dass das [Array (ndarray)] von numpy (https://docs.scipy.org/doc/numpy/reference/arrays.html) in C-Sprache implementiert ist, also habe ich gelesen, dass es sich um eine Art C-Sprache handelt. Seien Sie eingeschränkt. Tatsächlich wird beim Erstellen von numpy.array dtype angegeben.

Im Falle von numpy kann dies durch finfo (Gleitkomma) und iinfo (Ganzzahl) numpy: Überlauffehler bestätigt werden -Fehler)

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

Sie können von überprüfen. float128 ist ein langes Double in C, nicht float128 (4x Gleitkomma). Das Verhalten von Überlauf

>>> 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])

Es wird. Fließkomma hat auch eine doppelte Präzisionsbarriere in Python, so dass es unvermeidlich erscheint, in Bezug auf Float überzulaufen. Da Python3 int jedoch mit unendlicher Genauigkeit verarbeiten kann, kann es versehentlich zu einem Überlauf oder einem unbeabsichtigten Mangel an Genauigkeit kommen. Wenn numpy.array Ganzzahlen verarbeiten soll, die die Genauigkeitsgrenze von np.int überschreiten, können Sie entweder dtype nicht angeben oder ein Objekt für dtype angeben.

Mit anderen Worten

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

Aber

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

Es wird. Wenn für das Objekt dtype angegeben (nicht angegeben) ist, können Gleitkommazahlen mit mehreren Längen, z. B. mpmath, verarbeitet werden.

Verwenden Sie numpy.array mit mehreren mehreren schwebenden Brüchen (mpmath).

Wie im Fall von int sollte dtype nicht angegeben sein (oder dtype = Objekt).

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

Wenn Sie in der Vergangenheit die Reihenfolge der Operationen geändert haben, werden Sie möglicherweise wütend, dass sie undefiniert war. Jetzt können Sie jedoch fast dieselben Operationen mit vier Regeln ausführen, ohne zu wissen, ob dtype object, np.float oder np.int (Array) ist. Wenn die Elemente von vier Regeln können). Wenn Sie eine Operation mit einem Array ausführen, dessen dtype np.float (oder np.int) ist, geht die Genauigkeit natürlich verloren.

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

Vergleich der Berechnungsgeschwindigkeit

In numpy.array liefern dtype = np.int und np.float die beste Leistung der Berechnungsgeschwindigkeit. Überprüfen Sie, wie langsam es ist, wenn 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)

Als nächstes folgt das Ausführungsergebnis

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

Wenn das Element des Arrays int ist, ist es bei Angabe als Objekt überwiegend langsam, aber andererseits ist float ungefähr doppelt so groß wie der zulässige Bereich. Betrachten Sie nun den Fall von mpmath. Da der standardmäßige formale Teil von mpmath 15 Stellen umfasst, vergleichen wir die Berechnungsgeschwindigkeit zwischen 15 Stellen und 100 Stellen des formalen Teils. Bei den oben genannten Einstellungen dauert es jedoch einige Stunden, sodass imax zweistellig gelöscht wird. Wenn Sie daher die Berechnungszeit mit 100 multiplizieren, um sie mit dem obigen Ergebnis zu vergleichen, können Sie sie grob vergleichen.

imax = 100

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

Als nächstes folgt das Ausführungsergebnis

15.098075151443481 [sec] -2.4139151442170714e-06 object

Da der Rechenaufwand mit mpmath und 15 Ziffern des formalen Teils auf 1/100 reduziert wird, dauert es ungefähr 100 Mal.

Übrigens verbessert die Definition von Powpow die Genauigkeit nicht. Denn wenn $ x ^ {1/3} $ gesetzt ist, wird der Exponententeil vom Python-Float ausgewertet, sodass die Zahlen nach 15 Ziffern bedeutungslos sind. Also habe ich Powpow ein wenig modifiziert

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)

Bei Ausführung als

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

Übrigens, wenn Sie mit elementweise ohne Verwendung von Array auswerten

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

Es wird.

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

Die Berechnungsgeschwindigkeit ist etwas weniger als 30% schneller als bei numpy.array, und es ist sauberer, numpy.array zu verwenden, das mit der Codierung vertraut ist.

Ausführungsumgebung

$ lscpu 
die Architektur:                      x86_64
CPU-Betriebsmodus:                      32-bit, 64-bit
Bytereihenfolge:                          Little Endian
CPU:                                 4
Liste der CPUs, die online sind: 0-3
Anzahl der Threads pro Kern:              1
Anzahl der Kerne pro Sockel:              4
Anzahl der Steckdosen:                          1
Anzahl der NUMA-Knoten:                       1
Hersteller-ID:                         AuthenticAMD
CPU-Familie:                      23
Modell-:                              17
Modellname:                            AMD Ryzen 3 2200G with Radeon Vega Graphics
Schritt:                        0
CPU MHz:                             1479.915
CPU maximal MHz:                        3500.0000
CPU Minimum MHz:                        1600.0000
BogoMIPS:                            7000.24
Virtualisierung:                              AMD-V
L1d-Cache:                      32K
L1i-Cache:                      64K
L2-Cache:                       512K
L3-Cache:                       4096K
NUMA-Knoten 0 CPU:                   0-3
Flagge:                              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

Verwenden Sie numpy.ndarray für die Auswertung mehrerer Längen
EP 26 Verwenden Sie Mehrfachvererbung nur für Mix-In-Dienstprogrammklassen
Verwenden Sie PySide für die HDA-Benutzeroberfläche