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

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

    old new  
    127127    """ 
    128128    m4ri_destroy_all_codes() 
    129129 
     130     
     131 
    130132cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense):   # dense or sparse 
    131133    r""" 
    132134    Dense matrix over GF(2). 
     
    255257 
    256258    def __hash__(self): 
    257259        """ 
     260        The has of a matrix is computed as $\oplus i*a_i$ where the $a_i$ are  
     261        the flattened entries in a matrix (by row, then by column).  
     262         
    258263        EXAMPLE: 
    259264            sage: B = random_matrix(GF(2),3,3) 
    260265            sage: B.set_immutable() 
     
    262267            {[0 1 0] 
    263268            [0 1 1] 
    264269            [0 0 0]: 0} 
     270             
     271            sage: M = random_matrix(GF(2), 123, 321) 
     272            sage: M.set_immutable() 
     273            sage: MZ = M.change_ring(ZZ) 
     274            sage: MZ.set_immutable() 
     275            sage: hash(M) == hash(MZ) 
     276            True 
     277            sage: MS = M.sparse_matrix() 
     278            sage: MS.set_immutable() 
     279            sage: hash(M) == hash(MS) 
     280            True 
    265281 
    266282        TEST: 
    267283            sage: A = matrix(GF(2),2,0) 
     
    269285            sage: hash(A) 
    270286            0 
    271287        """ 
    272         return self._hash() 
    273      
     288        if not self._mutability._is_immutable: 
     289            raise TypeError, "mutable matrices are unhashable" 
     290             
     291        x = self.fetch('hash') 
     292        if not x is None: 
     293            return x 
     294             
     295        if self._nrows == 0 or self._ncols == 0: 
     296            return 0 
     297 
     298        cdef unsigned long i, j, truerow 
     299        cdef unsigned long start, shift 
     300        cdef word row_xor 
     301        cdef word end_mask = ~(((<word>1)<<(RADIX - self._ncols%RADIX))-1) 
     302        cdef word top_mask, bot_mask 
     303        cdef word cur 
     304        cdef word* row 
     305         
     306        # running_xor is the xor of all words in the matrix, as if the rows 
     307        # in the matrix were written out consecutively, without regard to  
     308        # word boundaries.  
     309        cdef word running_xor = 0 
     310        # running_parity is the number of extra words that must be xor'd. 
     311        cdef unsigned long running_parity = 0 
     312         
     313         
     314        for i from 0 <= i < self._entries.nrows: 
     315 
     316            # All this shifting and masking os because the 
     317            # rows are word-alligned.  
     318            truerow = self._entries.rowswap[i] 
     319            row = self._entries.values + truerow 
     320            start = (i*self._entries.ncols) >> 6 
     321            shift = (i*self._entries.ncols) & 0x3F 
     322            bot_mask = (~(<word>0)) << shift 
     323            top_mask = ~bot_mask 
     324 
     325            if self._entries.width > 1: 
     326                row_xor = row[0] 
     327                running_parity ^= start & parity_mask(row[0] & bot_mask) 
     328 
     329                for j from 1 <= j < self._entries.width - 1: 
     330                    row_xor ^= row[j] 
     331                    cur = ((row[j-1] << (63-shift)) << 1) ^ (row[j] >> shift) 
     332                    running_parity ^= (start+j) & parity_mask(cur) 
     333 
     334                running_parity ^= (start+j) & parity_mask(row[j-1] & top_mask) 
     335 
     336            else: 
     337                j = 0 
     338                row_xor = 0 
     339 
     340            cur = row[j] & end_mask 
     341            row_xor ^= cur 
     342            running_parity ^= (start+j) & parity_mask(cur & bot_mask) 
     343            running_parity ^= (start+j+1) & parity_mask(cur & top_mask) 
     344 
     345            start = (i*self._entries.ncols) & (RADIX-1) 
     346            running_xor ^= (row_xor >> start) ^ ((row_xor << (RADIX-1-start)) << 1) 
     347 
     348        cdef unsigned long bit_is_set 
     349        cdef unsigned long h 
     350 
     351        # Now we assemble the running_parity and running_xor to get the hash.  
     352        # Viewing the flattened matrix as a list of a_i, the hash is the xor 
     353        # of the i for which a_i is non-zero. We split i into the lower RADIX  
     354        # bits and the rest, so i = i1 << RADIX + i0. Now two matching i0  
     355        # would cancel, so we only need the parity of how many of each  
     356        # possible i0 occur. This is stored in the bits of running_xor. 
     357        # Similarly, running_parity is the xor of the i1 needed. It's called 
     358        # parity because i1 is constant accross a word, and for each word 
     359        # the number of i1 to add is equal to the number of set bits in that  
     360        # word (but because two cancel, we only need keep track of the  
     361        # parity.  
     362        h = RADIX * running_parity 
     363        for i from 0 <= i < RADIX: 
     364            bit_is_set = (running_xor >> (RADIX-1-i)) & 1 
     365            h ^= (RADIX-1) & ~(bit_is_set-1) & i 
     366 
     367        if h == -1: 
     368            h = -2 
     369 
     370        self.cache('hash', h) 
     371        return h 
     372 
     373 
    274374    cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value): 
    275375        mzd_write_bit(self._entries, i, j, int(value)) 
    276376 
     
    12221322        A._entries = mzd_submatrix(A._entries, self._entries, lowr, lowc, highr, highc) 
    12231323        return A 
    12241324 
     1325 
     1326 
     1327 
     1328# Used for hashing 
     1329cdef int i, k 
     1330cdef unsigned long parity_table[256] 
     1331for i from 0 <= i < 256: 
     1332    parity_table[i] = 1 & ((i) ^ (i>>1) ^ (i>>2) ^ (i>>3) ^  
     1333                           (i>>4) ^ (i>>5) ^ (i>>6) ^ (i>>7)) 
     1334                            
     1335# gmp's ULONG_PARITY may use special  
     1336# assembly instructions, could be faster 
     1337cpdef inline unsigned long parity(word a): 
     1338    """ 
     1339    Returns the parity of the number of bits in a.  
     1340     
     1341    EXAMPLES:  
     1342        sage: from sage.matrix.matrix_mod2_dense import parity 
     1343        sage: parity(1) 
     1344        1L 
     1345        sage: parity(3) 
     1346        0L 
     1347        sage: parity(0x10000101011) 
     1348        1L 
     1349    """ 
     1350    if sizeof(word) == 8: 
     1351        a ^= a >> 32 
     1352    a ^= a >> 16 
     1353    a ^= a >> 8 
     1354    return parity_table[a & 0xFF] 
     1355 
     1356cdef inline unsigned long parity_mask(word a): 
     1357    return -parity(a)