2012-07-02 9 views
30

La mia impressione è che in NumPy due array possano condividere la stessa memoria. Prendiamo il seguente esempio:C'è un modo per verificare se gli array di NumPy condividono gli stessi dati?

import numpy as np 
a=np.arange(27) 
b=a.reshape((3,3,3)) 
a[0]=5000 
print (b[0,0,0]) #5000 

#Some tests: 
a.data is b.data #False 
a.data == b.data #True 

c=np.arange(27) 
c[0]=5000 
a.data == c.data #True (Same data, not same memory storage), False positive 

Quindi, chiaramente b non ha fatto una copia di a; ha appena creato alcuni nuovi metadati e lo ha allegato allo stesso buffer di memoria che utilizza a. C'è un modo per verificare se due array fanno riferimento allo stesso buffer di memoria?

La mia prima impressione è stata quella di utilizzare a.data is b.data, ma che restituisce false. Posso fare a.data == b.data che restituisce True, ma non penso che controlli per assicurarsi che a e condividano lo stesso buffer di memoria, solo che il blocco di memoria a cui fa riferimento a e quello a cui fa riferimento b hanno gli stessi byte.

+2

Ecco la domanda più pertinente in precedenza: http://stackoverflow.com/questions/10747748/how-do-i-check-that-two-slices-of-numpy-arrays-are-the-same- o sovrapposti –

+1

@RobertKern - Grazie. In realtà avevo visto quel post, ma dal momento che non riuscivo a trovare la documentazione per 'numpy.may_share_memory' (oltre che l''aiuto' incorporato), pensavo che potesse esserci qualcos'altro - ad es. 'Numpy.uses_same_memory_exactly'. (il mio caso d'uso è leggermente meno generale dell'altro, quindi ho pensato che potesse esserci una risposta più definitiva). Ad ogni modo, avendo visto il tuo nome su alcune numere mailing list, immagino che la risposta sia "non esiste una tale funzione". – mgilson

+1

'numpy.may_share_memory()' non compare nel manuale di riferimento solo a causa di un incidente dell'organizzazione del manuale di riferimento. È la cosa giusta da usare Sfortunatamente, al momento non esiste una funzione 'uses_same_memory_exactly()'. Per implementare tale funzione è necessario risolvere un'equazione Diofanteina lineare delimitata, un problema NP-difficile. La dimensione del problema di solito non è troppo grande, ma solo scrivere l'algoritmo è fastidioso, quindi non è stato ancora fatto. Se lo facciamo, sarà incorporato in 'numpy.may_share_memory() ', quindi è quello che consiglio di usare. –

risposta

8

penso la risposta di jterrace è probabilmente il modo migliore per andare, ma qui è un'altra possibilità.

def byte_offset(a): 
    """Returns a 1-d array of the byte offset of every element in `a`. 
    Note that these will not in general be in order.""" 
    stride_offset = np.ix_(*map(range,a.shape)) 
    element_offset = sum(i*s for i, s in zip(stride_offset,a.strides)) 
    element_offset = np.asarray(element_offset).ravel() 
    return np.concatenate([element_offset + x for x in range(a.itemsize)]) 

def share_memory(a, b): 
    """Returns the number of shared bytes between arrays `a` and `b`.""" 
    a_low, a_high = np.byte_bounds(a) 
    b_low, b_high = np.byte_bounds(b) 

    beg, end = max(a_low,b_low), min(a_high,b_high) 

    if end - beg > 0: 
     # memory overlaps 
     amem = a_low + byte_offset(a) 
     bmem = b_low + byte_offset(b) 

     return np.intersect1d(amem,bmem).size 
    else: 
     return 0 

Esempio:

>>> a = np.arange(10) 
>>> b = a.reshape((5,2)) 
>>> c = a[::2] 
>>> d = a[1::2] 
>>> e = a[0:1] 
>>> f = a[0:1] 
>>> f = f.reshape(()) 
>>> share_memory(a,b) 
80 
>>> share_memory(a,c) 
40 
>>> share_memory(a,d) 
40 
>>> share_memory(c,d) 
0 
>>> share_memory(a,e) 
8 
>>> share_memory(a,f) 
8 

Ecco un grafico che mostra il tempo per ogni share_memory(a,a[::2]) chiamata come una funzione del numero di elementi in a sul mio computer.

share_memory function

+4

Si possono avere viste che condividono la memoria anche con elementi differenti. Per esempio, potrei ottenere una matrice come 'float32' con componenti reali e immaginari interlacciati e vederla come una matrice' complex64'. Un'implementazione più affidabile è in 'numpy.may_share_memory()'. –

+0

@RobertKern: buon punto. Ho aggiornato la mia risposta. Vedi qualche potenziale problema con questa soluzione? – user545424

+0

Penso di aver finalmente capito bene. 'share_memory()' richiede memoria nell'ordine di somma delle dimensioni di ogni array, ma è piuttosto veloce. – user545424

26

È possibile utilizzare l'attributo base per verificare se una matrice condivide la memoria con un altro array:

>>> import numpy as np 
>>> a = np.arange(27) 
>>> b = a.reshape((3,3,3)) 
>>> b.base is a 
True 
>>> a.base is b 
False 

Non sono sicuro se questo risolve il problema. L'attributo base sarà None se l'array possiede la propria memoria. Si noti che la base di una matrice sarà un altro array, anche se si tratta di un sottoinsieme:

>>> c = a[2:] 
>>> c.base is a 
True 
+0

Probabilmente è abbastanza buono per i miei scopi. È sfortunato che non è comunque una strada a doppio senso. Aspetterò e vedrò se qualcosa di meglio apparirà. Nel frattempo, grazie. (+1) – mgilson

+0

Si potrebbe fare 'a.base è b o b.base is a'. – user545424

+4

Questo non è affidabile. Ogni array può avere catene di attributi '.base', ad es. 'a.base.base is b' potrebbe essere vero. Gli array possono anche essere costruiti per puntare alla stessa memoria senza condividere gli stessi oggetti '.base'. –

4

Basta fare:

a = np.arange(27) 
a.__array_interface__['data'] 

La seconda riga restituirà una tupla, dove la prima voce è l'indirizzo di memoria e la seconda è se la matrice è di sola lettura. Combinato con la forma e il tipo di dati, è possibile capire l'esatto intervallo di indirizzi di memoria che l'array copre, in modo da poter anche risolvere da questo quando un array è un sottoinsieme di un altro.

Problemi correlati