nodes: 0, module; 1, function_definition; 2, function_name:hashable_rows; 3, parameters; 4, block; 5, identifier:data; 6, default_parameter; 7, expression_statement; 8, comment:# if there is no data return immediately; 9, if_statement; 10, comment:# get array as integer to precision we care about; 11, expression_statement; 12, comment:# if it is flat integers already, return; 13, if_statement; 14, comment:# if array is 2D and smallish, we can try bitbanging; 15, comment:# this is significantly faster than the custom dtype; 16, if_statement; 17, comment:# reshape array into magical data type that is weird but hashable; 18, expression_statement; 19, comment:# make sure result is contiguous and flat; 20, expression_statement; 21, return_statement; 22, identifier:digits; 23, None; 24, comment:"""
      We turn our array into integers based on the precision
      given by digits and then put them in a hashable format.
  
      Parameters
      ---------
      data : (n, m) array
        Input data
      digits : int or None
        How many digits to add to hash if data is floating point
        If None, tol.merge will be used
  
      Returns
      ---------
      hashable : (n,) array
        Custom data type which can be sorted
        or used as hash keys
      """; 25, comparison_operator:len(data) == 0; 26, block; 27, assignment; 28, comparison_operator:len(as_int.shape) == 1; 29, block; 30, boolean_operator; 31, comment:# time for some righteous bitbanging; 32, comment:# can we pack the whole row into a single 64 bit integer; 33, block; 34, assignment; 35, assignment; 36, identifier:hashable; 37, call; 38, integer:0; 39, return_statement; 40, identifier:as_int; 41, call; 42, call; 43, integer:1; 44, return_statement; 45, comparison_operator:len(as_int.shape) == 2; 46, comparison_operator:as_int.shape[1] <= 4; 47, expression_statement; 48, comment:# if the max value is less than precision we can do this; 49, if_statement; 50, identifier:dtype; 51, call; 52, identifier:hashable; 53, call; 54, identifier:len; 55, argument_list; 56, call; 57, identifier:float_to_int; 58, argument_list; 59, identifier:len; 60, argument_list; 61, identifier:as_int; 62, call; 63, integer:2; 64, subscript; 65, integer:4; 66, assignment; 67, comparison_operator:np.abs(as_int).max() < 2**(precision - 1); 68, comment:# the resulting package; 69, block; 70, attribute; 71, argument_list; 72, attribute; 73, argument_list; 74, identifier:data; 75, attribute; 76, argument_list; 77, identifier:data; 78, keyword_argument; 79, attribute; 80, identifier:len; 81, argument_list; 82, attribute; 83, integer:1; 84, identifier:precision; 85, call; 86, call; 87, binary_operator:2**(precision - 1); 88, expression_statement; 89, comment:# loop through each column and bitwise xor to combine; 90, comment:# make sure as_int is int64 otherwise bit offset won't work; 91, for_statement; 92, return_statement; 93, identifier:np; 94, identifier:dtype; 95, tuple; 96, call; 97, identifier:reshape; 98, unary_operator; 99, identifier:np; 100, identifier:array; 101, list; 102, identifier:digits; 103, identifier:digits; 104, identifier:as_int; 105, identifier:shape; 106, attribute; 107, identifier:as_int; 108, identifier:shape; 109, identifier:int; 110, argument_list; 111, attribute; 112, argument_list; 113, integer:2; 114, parenthesized_expression; 115, assignment; 116, pattern_list; 117, call; 118, comment:# will modify hashable in place; 119, block; 120, identifier:hashable; 121, attribute; 122, binary_operator:as_int.dtype.itemsize * as_int.shape[1]; 123, attribute; 124, argument_list; 125, integer:1; 126, identifier:as_int; 127, identifier:shape; 128, call; 129, call; 130, identifier:max; 131, binary_operator:precision - 1; 132, identifier:hashable; 133, call; 134, identifier:offset; 135, identifier:column; 136, identifier:enumerate; 137, argument_list; 138, expression_statement; 139, identifier:np; 140, identifier:void; 141, attribute; 142, subscript; 143, call; 144, identifier:view; 145, identifier:dtype; 146, attribute; 147, argument_list; 148, attribute; 149, argument_list; 150, identifier:precision; 151, integer:1; 152, attribute; 153, argument_list; 154, attribute; 155, call; 156, attribute; 157, identifier:itemsize; 158, attribute; 159, integer:1; 160, attribute; 161, argument_list; 162, identifier:np; 163, identifier:floor; 164, binary_operator:64 / as_int.shape[1]; 165, identifier:np; 166, identifier:abs; 167, identifier:as_int; 168, identifier:np; 169, identifier:zeros; 170, call; 171, keyword_argument; 172, call; 173, identifier:T; 174, attribute; 175, argument_list; 176, identifier:as_int; 177, identifier:dtype; 178, identifier:as_int; 179, identifier:shape; 180, identifier:np; 181, identifier:ascontiguousarray; 182, identifier:as_int; 183, integer:64; 184, subscript; 185, identifier:len; 186, argument_list; 187, identifier:dtype; 188, attribute; 189, attribute; 190, argument_list; 191, identifier:np; 192, identifier:bitwise_xor; 193, identifier:hashable; 194, binary_operator:column << (offset * precision); 195, keyword_argument; 196, attribute; 197, integer:1; 198, identifier:as_int; 199, identifier:np; 200, identifier:int64; 201, identifier:as_int; 202, identifier:astype; 203, attribute; 204, identifier:column; 205, parenthesized_expression; 206, identifier:out; 207, identifier:hashable; 208, identifier:as_int; 209, identifier:shape; 210, identifier:np; 211, identifier:int64; 212, binary_operator:offset * precision; 213, identifier:offset; 214, identifier:precision
code: def hashable_rows(data, digits=None):
      """
      We turn our array into integers based on the precision
      given by digits and then put them in a hashable format.
  
      Parameters
      ---------
      data : (n, m) array
        Input data
      digits : int or None
        How many digits to add to hash if data is floating point
        If None, tol.merge will be used
  
      Returns
      ---------
      hashable : (n,) array
        Custom data type which can be sorted
        or used as hash keys
      """
      # if there is no data return immediately
      if len(data) == 0:
          return np.array([])
  
      # get array as integer to precision we care about
      as_int = float_to_int(data, digits=digits)
  
      # if it is flat integers already, return
      if len(as_int.shape) == 1:
          return as_int
  
      # if array is 2D and smallish, we can try bitbanging
      # this is significantly faster than the custom dtype
      if len(as_int.shape) == 2 and as_int.shape[1] <= 4:
          # time for some righteous bitbanging
          # can we pack the whole row into a single 64 bit integer
          precision = int(np.floor(64 / as_int.shape[1]))
          # if the max value is less than precision we can do this
          if np.abs(as_int).max() < 2**(precision - 1):
              # the resulting package
              hashable = np.zeros(len(as_int), dtype=np.int64)
              # loop through each column and bitwise xor to combine
              # make sure as_int is int64 otherwise bit offset won't work
              for offset, column in enumerate(as_int.astype(np.int64).T):
                  # will modify hashable in place
                  np.bitwise_xor(hashable,
                                 column << (offset * precision),
                                 out=hashable)
              return hashable
  
      # reshape array into magical data type that is weird but hashable
      dtype = np.dtype((np.void, as_int.dtype.itemsize * as_int.shape[1]))
      # make sure result is contiguous and flat
      hashable = np.ascontiguousarray(as_int).view(dtype).reshape(-1)
      return hashable