Ticket #3969: 3969-fast-matmod2-hash-rebased.patch

File 3969-fast-matmod2-hash-rebased.patch, 7.0 kB (added by malb, 4 months ago)
  • a/sage/matrix/matrix_mod2_dense.pyx

    old new  
    145145    """ 
    146146    m4ri_destroy_all_codes() 
    147147 
     148     
     149 
    148150cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense):   # dense or sparse 
    149151    r""" 
    150152    Dense matrix over GF(2). 
     
    273275 
    274276    def __hash__(self): 
    275277        """ 
     278        The has of a matrix is computed as $\oplus i*a_i$ where the $a_i$ are  
     279        the flattened entries in a matrix (by row, then by column).  
     280         
    276281        EXAMPLE: 
    277282            sage: B = random_matrix(GF(2),3,3) 
    278283            sage: B.set_immutable() 
     
    280285            {[0 1 0] 
    281286            [0 1 1] 
    282287            [0 0 0]: 0} 
    283             sage: A = matrix(GF(2),2,2) 
    284             sage: A.set_immutable() 
    285             sage: hex(hash(A)) 
    286             '0xdeadbeed' # 64-bit 
    287288            '-0x21524113' # 32-bit 
     289            sage: M = random_matrix(GF(2), 123, 321) 
     290            sage: M.set_immutable() 
     291            sage: MZ = M.change_ring(ZZ) 
     292            sage: MZ.set_immutable() 
     293            sage: hash(M) == hash(MZ) 
     294            True 
     295            sage: MS = M.sparse_matrix() 
     296            sage: MS.set_immutable() 
     297            sage: hash(M) == hash(MS) 
     298            True 
    288299             
    289300        TEST: 
    290301            sage: A = matrix(GF(2),2,0) 
     
    297308            0 
    298309             
    299310        """ 
    300         if self.is_mutable(): 
    301             raise TypeError("mutable matrices are unhashable") 
    302         cdef unsigned long _hash = 0xDEADBEEF 
    303         cdef unsigned long counter = 0 
    304         cdef unsigned long i, j, truerow 
    305         cdef word mask = 1 
    306         mask = ~((mask<<(RADIX - self._ncols%RADIX))-1) 
    307  
     311        if not self._mutability._is_immutable: 
     312            raise TypeError, "mutable matrices are unhashable" 
     313             
     314        x = self.fetch('hash') 
     315        if not x is None: 
     316            return x 
     317             
    308318        if self._nrows == 0 or self._ncols == 0: 
    309319            return 0 
    310320 
     321        cdef unsigned long i, j, truerow 
     322        cdef unsigned long start, shift 
     323        cdef word row_xor 
     324        cdef word end_mask = ~(((<word>1)<<(RADIX - self._ncols%RADIX))-1) 
     325        cdef word top_mask, bot_mask 
     326        cdef word cur 
     327        cdef word* row 
     328         
     329        # running_xor is the xor of all words in the matrix, as if the rows 
     330        # in the matrix were written out consecutively, without regard to  
     331        # word boundaries.  
     332        cdef word running_xor = 0 
     333        # running_parity is the number of extra words that must be xor'd. 
     334        cdef unsigned long running_parity = 0 
     335         
     336         
    311337        for i from 0 <= i < self._entries.nrows: 
     338 
     339            # All this shifting and masking is because the 
     340            # rows are word-aligned.  
    312341            truerow = self._entries.rowswap[i] 
    313             for j from 0 <= j < self._entries.width - 1: 
    314                 _hash ^= self._entries.values[truerow + j] 
    315                 counter += 1 
    316             _hash ^= self._entries.values[truerow + j] & mask 
    317             counter += 1 
    318                  
    319         _hash = _hash ^ counter 
     342            row = self._entries.values + truerow 
     343            start = (i*self._entries.ncols) >> 6 
     344            shift = (i*self._entries.ncols) & 0x3F 
     345            bot_mask = (~(<word>0)) << shift 
     346            top_mask = ~bot_mask 
    320347 
    321         if _hash == -1: 
    322             return -2 
    323         return _hash 
     348            if self._entries.width > 1: 
     349                row_xor = row[0] 
     350                running_parity ^= start & parity_mask(row[0] & bot_mask) 
     351 
     352                for j from 1 <= j < self._entries.width - 1: 
     353                    row_xor ^= row[j] 
     354                    cur = ((row[j-1] << (63-shift)) << 1) ^ (row[j] >> shift) 
     355                    running_parity ^= (start+j) & parity_mask(cur) 
     356 
     357                running_parity ^= (start+j) & parity_mask(row[j-1] & top_mask) 
     358 
     359            else: 
     360                j = 0 
     361                row_xor = 0 
     362 
     363            cur = row[j] & end_mask 
     364            row_xor ^= cur 
     365            running_parity ^= (start+j) & parity_mask(cur & bot_mask) 
     366            running_parity ^= (start+j+1) & parity_mask(cur & top_mask) 
     367 
     368            start = (i*self._entries.ncols) & (RADIX-1) 
     369            running_xor ^= (row_xor >> start) ^ ((row_xor << (RADIX-1-start)) << 1) 
     370 
     371        cdef unsigned long bit_is_set 
     372        cdef unsigned long h 
     373 
     374        # Now we assemble the running_parity and running_xor to get the hash.  
     375        # Viewing the flattened matrix as a list of a_i, the hash is the xor 
     376        # of the i for which a_i is non-zero. We split i into the lower RADIX  
     377        # bits and the rest, so i = i1 << RADIX + i0. Now two matching i0  
     378        # would cancel, so we only need the parity of how many of each  
     379        # possible i0 occur. This is stored in the bits of running_xor. 
     380        # Similarly, running_parity is the xor of the i1 needed. It's called 
     381        # parity because i1 is constant accross a word, and for each word 
     382        # the number of i1 to add is equal to the number of set bits in that  
     383        # word (but because two cancel, we only need keep track of the  
     384        # parity.  
     385        h = RADIX * running_parity 
     386        for i from 0 <= i < RADIX: 
     387            bit_is_set = (running_xor >> (RADIX-1-i)) & 1 
     388            h ^= (RADIX-1) & ~(bit_is_set-1) & i 
     389 
     390        if h == -1: 
     391            h = -2 
     392 
     393        self.cache('hash', h) 
     394        return h 
    324395     
    325396    cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value): 
    326397        mzd_write_bit(self._entries, i, j, int(value)) 
     
    13321403        gdFree(buf) 
    13331404        return unpickle_matrix_mod2_dense_v1, (r,c, data, size) 
    13341405 
     1406 
     1407# Used for hashing 
     1408cdef int i, k 
     1409cdef unsigned long parity_table[256] 
     1410for i from 0 <= i < 256: 
     1411    parity_table[i] = 1 & ((i) ^ (i>>1) ^ (i>>2) ^ (i>>3) ^  
     1412                           (i>>4) ^ (i>>5) ^ (i>>6) ^ (i>>7)) 
     1413                            
     1414# gmp's ULONG_PARITY may use special  
     1415# assembly instructions, could be faster 
     1416cpdef inline unsigned long parity(word a): 
     1417    """ 
     1418    Returns the parity of the number of bits in a.  
     1419     
     1420    EXAMPLES:  
     1421        sage: from sage.matrix.matrix_mod2_dense import parity 
     1422        sage: parity(1) 
     1423        1L 
     1424        sage: parity(3) 
     1425        0L 
     1426        sage: parity(0x10000101011) 
     1427        1L 
     1428    """ 
     1429    if sizeof(word) == 8: 
     1430        a ^= a >> 32 
     1431    a ^= a >> 16 
     1432    a ^= a >> 8 
     1433    return parity_table[a & 0xFF] 
     1434 
     1435cdef inline unsigned long parity_mask(word a): 
     1436    return -parity(a) 
     1437 
     1438 
    13351439def unpickle_matrix_mod2_dense_v1(r, c, data, size): 
    13361440    r""" 
    13371441    Deserialize a matrix encoded in the string \code{s}.