def test_export_mkl_bad_type(self): mkl_handle, dbl = _create_mkl_sparse(self.mat1) with self.assertRaises(ValueError): _export_mkl(mkl_handle, dbl, output_type="coo") _destroy_mkl_handle(mkl_handle)
def _sparse_dense_matmul(matrix_a, matrix_b, scalar=1., transpose=False): """ Multiply together a sparse and a dense matrix mkl_sparse_?_mm requires the left (A) matrix to be sparse and the right (B) matrix to be dense This requires conversion of the sparse matrix to CSR format for some dense arrays. A must be CSR if B is column-major. Otherwise CSR or CSC are acceptable. :param matrix_a: Left (A) matrix :type matrix_a: sp.spmatrix.csr, sp.spmatrix.csc :param matrix_b: Right (B) matrix :type matrix_b: np.ndarray :param scalar: A value to multiply the result matrix by. Defaults to 1. :type scalar: float :param transpose: Return AT (dot) B instead of A (dot) B. :type transpose: bool :return: A (dot) B as a dense array in either column-major or row-major format :rtype: np.ndarray """ output_shape = (matrix_a.shape[1] if transpose else matrix_a.shape[0], matrix_b.shape[1]) layout_b, ld_b = _get_numpy_layout(matrix_b) # Prep MKL handles and check that matrixes are compatible types # MKL requires CSR format if the dense array is column-major if layout_b == LAYOUT_CODE_F and not _spsparse.isspmatrix_csr(matrix_a): mkl_non_csr, dbl = _create_mkl_sparse(matrix_a) mkl_a = _convert_to_csr(mkl_non_csr) else: mkl_a, dbl = _create_mkl_sparse(matrix_a) # Set functions and types for float or doubles output_ctype = _ctypes.c_double if dbl else _ctypes.c_float output_dtype = np.float64 if dbl else np.float32 func = MKL._mkl_sparse_d_mm if dbl else MKL._mkl_sparse_s_mm # Allocate an output array output_arr = np.zeros(output_shape, dtype=output_dtype, order="C" if layout_b == LAYOUT_CODE_C else "F") _, output_ld = _get_numpy_layout(output_arr) ret_val = func(11 if transpose else 10, scalar, mkl_a, matrix_descr(), layout_b, matrix_b, output_shape[1], ld_b, 1., output_arr.ctypes.data_as(_ctypes.POINTER(output_ctype)), output_ld) # Check return if ret_val != 0: err_msg = "{fn} returned {v} ({e})".format(fn=func.__name__, v=ret_val, e=RETURN_CODES[ret_val]) raise ValueError(err_msg) _destroy_mkl_handle(mkl_a) return output_arr
def test_empty_handle(self): mkl_handle_empty = sparse_matrix_t() with self.assertRaises(ValueError): _export_mkl(mkl_handle_empty, True, output_type="csr") with self.assertRaises(ValueError): _convert_to_csr(mkl_handle_empty) with self.assertRaises(ValueError): _order_mkl_handle(mkl_handle_empty) with self.assertRaises(ValueError): _destroy_mkl_handle(mkl_handle_empty)
def _sparse_dense_vector_mult(matrix_a, vector_b, scalar=1., transpose=False): """ Multiply together a sparse matrix and a dense vector :param matrix_a: Left (A) matrix :type matrix_a: sp.spmatrix.csr, sp.spmatrix.csc :param vector_b: Right (B) vector with shape (N, ) or (N, 1) :type vector_b: np.ndarray :param scalar: A value to multiply the result matrix by. Defaults to 1. :type scalar: float :param transpose: Return AT (dot) B instead of A (dot) B. :type transpose: bool :return: A (dot) B as a dense array :rtype: np.ndarray """ output_shape = matrix_a.shape[1] if transpose else matrix_a.shape[0] output_shape = (output_shape, ) if vector_b.ndim == 1 else (output_shape, 1) if _empty_output_check(matrix_a, vector_b): final_dtype = np.float64 if matrix_a.dtype != vector_b.dtype or matrix_a.dtype != np.float32 else np.float32 return np.zeros(output_shape, dtype=final_dtype) mkl_a, dbl = _create_mkl_sparse(matrix_a) vector_b = vector_b.ravel() # Set functions and types for float or doubles output_ctype = _ctypes.c_double if dbl else _ctypes.c_float output_dtype = np.float64 if dbl else np.float32 func = MKL._mkl_sparse_d_mv if dbl else MKL._mkl_sparse_s_mv output_arr = np.zeros(output_shape, dtype=output_dtype) ret_val = func(11 if transpose else 10, scalar, mkl_a, matrix_descr(), vector_b, 1., output_arr.ctypes.data_as(_ctypes.POINTER(output_ctype))) # Check return if ret_val != 0: err_msg = "{fn} returned {v} ({e})".format(fn=func.__name__, v=ret_val, e=RETURN_CODES[ret_val]) raise ValueError(err_msg) _destroy_mkl_handle(mkl_a) return output_arr
def _gram_matrix_sparse(matrix_a, aat=False, reorder_output=False): """ Calculate the gram matrix aTa for sparse matrix and return a sparse matrix :param matrix_a: Sparse matrix :type matrix_a: scipy.sparse.csr_matrix, scipy.sparse.csc_matrix :param aat: Return A (dot) AT instead of AT (dot) A :type aat: bool :param reorder_output: :type reorder_output: bool :return: Sparse matrix :rtype: scipy.sparse.csr_matrix """ sp_ref_a, double_prec = _create_mkl_sparse(matrix_a) if _sps.isspmatrix_csc(matrix_a): sp_ref_a = _convert_to_csr(sp_ref_a) _order_mkl_handle(sp_ref_a) ref_handle = sparse_matrix_t() ret_val = MKL._mkl_sparse_syrk(10 if aat else 11, sp_ref_a, _ctypes.byref(ref_handle)) # Check return if ret_val != 0: _err_msg = "mkl_sparse_syrk returned {v} ({e})".format( v=ret_val, e=RETURN_CODES[ret_val]) if ret_val == 2: _err_msg += "; Try changing MKL to int64 with the environment variable MKL_INTERFACE_LAYER=ILP64" raise ValueError(_err_msg) if reorder_output: _order_mkl_handle(ref_handle) output_arr = _export_mkl(ref_handle, double_prec, output_type="csr") _destroy_mkl_handle(sp_ref_a) _destroy_mkl_handle(ref_handle) return output_arr
def _sparse_dot_sparse(matrix_a, matrix_b, cast=False, reorder_output=False, dense=False, dprint=print): """ Multiply together two scipy sparse matrixes using the intel Math Kernel Library. This currently only supports float32 and float64 data :param matrix_a: Sparse matrix A in CSC/CSR format :type matrix_a: scipy.sparse.spmatrix :param matrix_b: Sparse matrix B in CSC/CSR format :type matrix_b: scipy.sparse.spmatrix :param cast: Should the data be coerced into float64 if it isn't float32 or float64 If set to True and any other dtype is passed, the matrix data will be modified in-place If set to False and any dtype that isn't float32 or float64 is passed, a ValueError will be raised Defaults to False :param reorder_output: Should the array indices be reordered using MKL If set to True, the object in C will be ordered and then exported into python If set to False, the array column indices will not be ordered. The scipy sparse dot product does not yield ordered column indices so this defaults to False :type reorder_output: bool :param dense: Should the matrix multiplication yield a dense numpy array This does not require any copy and is memory efficient if the output array density is > 50% :type dense: bool :param dprint: Should debug and timing messages be printed. Defaults to false. :type dprint: function :return: Sparse matrix that is the result of A * B in CSR format :rtype: scipy.sparse.csr_matrix """ # Check for allowed sparse matrix types if is_csr(matrix_a) and (is_csc(matrix_b) or is_csr(matrix_b)): default_output = _spsparse.csr_matrix output_type = "csr" elif is_csc(matrix_a) and (is_csc(matrix_b) or is_csr(matrix_b)): default_output = _spsparse.csc_matrix output_type = "csc" else: raise ValueError( "Both input matrices to dot_product_mkl must be CSR or CSC; COO and BSR are not supported" ) # Override output if dense flag is set default_output = default_output if not dense else np.zeros # Check to make sure that this multiplication can work and check dtypes _sanity_check(matrix_a, matrix_b) # Check for edge condition inputs which result in empty outputs if _empty_output_check(matrix_a, matrix_b): dprint( "Skipping multiplication because A (dot) B must yield an empty matrix" ) final_dtype = np.float64 if matrix_a.dtype != matrix_b.dtype or matrix_a.dtype != np.float32 else np.float32 return default_output((matrix_a.shape[0], matrix_b.shape[1]), dtype=final_dtype) # Check dtypes matrix_a, matrix_b = _type_check(matrix_a, matrix_b, cast=cast, dprint=dprint) t0 = time.time() # Create intel MKL objects mkl_a, a_dbl = _create_mkl_sparse(matrix_a) mkl_b, b_dbl = _create_mkl_sparse(matrix_b) t1 = time.time() dprint("Created MKL sparse handles: {0:.6f} seconds".format(t1 - t0)) # Call spmmd for dense output directly if the dense flag is set if dense: dense_arr = _matmul_mkl_dense(mkl_a, mkl_b, (matrix_a.shape[0], matrix_b.shape[1]), a_dbl or b_dbl) t2 = time.time() dprint("Multiplied matrices: {0:.6f} seconds".format(t2 - t1)) _destroy_mkl_handle(mkl_a) _destroy_mkl_handle(mkl_b) return dense_arr # Call spmm for sparse output if the dense flag is not set and then export the sparse matrix to python else: # Dot product mkl_c = _matmul_mkl(mkl_a, mkl_b) _destroy_mkl_handle(mkl_a) _destroy_mkl_handle(mkl_b) t2 = time.time() dprint("Multiplied matrices: {0:.6f} seconds".format(t2 - t1)) # Reorder if reorder_output: _order_mkl_handle(mkl_c) dprint("Reordered indicies: {0:.6f} seconds".format(time.time() - t2)) t2 = time.time() # Extract python_c = _export_mkl(mkl_c, a_dbl or b_dbl, output_type=output_type) _destroy_mkl_handle(mkl_c) dprint("Created python handle: {0:.6f} seconds".format(time.time() - t2)) return python_c
def _sparse_dot_transpose(matrix_a, cast=False, reorder_output=False, dprint=print): """ Multiply together two scipy sparse matrixes using the intel Math Kernel Library. This currently only supports float32 and float64 data :param matrix_a: Sparse matrix A in CSC/CSR format :type matrix_a: scipy.sparse.spmatrix :param cast: Should the data be coerced into float64 if it isn't float32 or float64 If set to True and any other dtype is passed, the matrix data will be modified in-place If set to False and any dtype that isn't float32 or float64 is passed, a ValueError will be raised Defaults to False :param reorder_output: Should the array indices be reordered using MKL If set to True, the object in C will be ordered and then exported into python If set to False, the array column indices will not be ordered. The scipy sparse dot product does not yield ordered column indices so this defaults to False :type reorder_output: bool :param dprint: Should debug and timing messages be printed. Defaults to false. :type dprint: function :return: Sparse matrix that is the result of A * B in CSR format :rtype: scipy.sparse.csr_matrix """ # Check for allowed sparse matrix types if is_csr(matrix_a): default_output = _spsparse.csr_matrix output_type = "csr" elif is_csc(matrix_a): default_output = _spsparse.csc_matrix output_type = "csc" else: raise ValueError( "Input matrix to dot_product_transpose_mkl must be CSR or CSC; COO and BSR are not supported" ) # Check for edge condition inputs which result in empty outputs if _empty_output_check(matrix_a, matrix_a.T): dprint( "Skipping multiplication because A (dot) A.T must yield an empty matrix" ) return default_output((matrix_a.shape[0], matrix_a.shape[1]), dtype=matrix_a.dtype) t0 = time.time() # Create intel MKL objects mkl_a, a_dbl = _create_mkl_sparse(matrix_a) t1 = time.time() dprint("Created MKL sparse handle: {0:.6f} seconds".format(t1 - t0)) # Dot product mkl_c = _syrk_mkl(mkl_a) _destroy_mkl_handle(mkl_a) t2 = time.time() dprint("Multiplied matrices: {0:.6f} seconds".format(t2 - t1)) # Reorder if reorder_output: _order_mkl_handle(mkl_c) dprint("Reordered indicies: {0:.6f} seconds".format(time.time() - t2)) t2 = time.time() # Extract python_c = _export_mkl(mkl_c, a_dbl, output_type=output_type) _destroy_mkl_handle(mkl_c) dprint("Created python handle: {0:.6f} seconds".format(time.time() - t2)) return python_c
def _sparse_qr(matrix_a, matrix_b): """ Solve AX = B for X :param matrix_a: Sparse matrix A :type matrix_a: scipy.sparse.csr_matrix :param matrix_b: Dense matrix B :type matrix_b: numpy.ndarray :return: Dense matrix X :rtype: numpy.ndarray """ mkl_a, dbl = _create_mkl_sparse(matrix_a) layout_b, ld_b = _get_numpy_layout(matrix_b) output_shape = matrix_a.shape[1], matrix_b.shape[1] if _spsparse.isspmatrix_csc(matrix_a): mkl_a = _convert_to_csr(mkl_a) # QR Reorder ## ret_val_r = MKL._mkl_sparse_qr_reorder(mkl_a, matrix_descr()) # Check return if ret_val_r != 0: err_msg = "mkl_sparse_qr_reorder returned {v} ({e})".format( v=ret_val_r, e=RETURN_CODES[ret_val_r]) raise ValueError(err_msg) # QR Factorize ## factorize_func = MKL._mkl_sparse_d_qr_factorize if dbl else MKL._mkl_sparse_s_qr_factorize ret_val_f = factorize_func(mkl_a, None) # Check return if ret_val_f != 0: err_msg = "{fn} returned {v} ({e})".format(fn=factorize_func.__name__, v=ret_val_f, e=RETURN_CODES[ret_val_f]) raise ValueError(err_msg) # QR Solve ## output_dtype = np.float64 if dbl else np.float32 output_ctype = _ctypes.c_double if dbl else _ctypes.c_float output_arr = np.zeros(output_shape, dtype=output_dtype, order="C" if layout_b == LAYOUT_CODE_C else "F") layout_out, ld_out = _get_numpy_layout(output_arr) solve_func = MKL._mkl_sparse_d_qr_solve if dbl else MKL._mkl_sparse_s_qr_solve ret_val_s = solve_func( 10, mkl_a, None, layout_b, output_shape[1], output_arr.ctypes.data_as(_ctypes.POINTER(output_ctype)), ld_out, matrix_b, ld_b) # Check return if ret_val_s != 0: err_msg = "{fn} returned {v} ({e})".format(fn=solve_func.__name__, v=ret_val_s, e=RETURN_CODES[ret_val_s]) raise ValueError(err_msg) _destroy_mkl_handle(mkl_a) return output_arr
def _gram_matrix_sparse_to_dense(matrix_a, aat=False, scalar=1., out=None, out_scalar=None): """ Calculate the gram matrix aTa for sparse matrix and return a dense matrix :param matrix_a: Sparse matrix :type matrix_a: scipy.sparse.csr_matrix, scipy.sparse.csc_matrix :param aat: Return A (dot) AT instead of AT (dot) A :type aat: bool :param scalar: Multiply output by a scalar value :type scalar: float :return: Dense matrix :rtype: numpy.ndarray """ sp_ref_a, double_prec = _create_mkl_sparse(matrix_a) if _sps.isspmatrix_csc(matrix_a): sp_ref_a = _convert_to_csr(sp_ref_a, destroy_original=True) _order_mkl_handle(sp_ref_a) out_dtype = np.float64 if double_prec else np.float32 output_ctype = _ctypes.c_double if double_prec else _ctypes.c_float out_dim = matrix_a.shape[0] if aat else matrix_a.shape[1] output_arr = _out_matrix((out_dim, out_dim), out_dtype, order="C", out_arr=out) _, output_ld = _get_numpy_layout(output_arr) if _empty_output_check(matrix_a, matrix_a): return output_arr func = MKL._mkl_sparse_d_syrkd if double_prec else MKL._mkl_sparse_s_syrkd ret_val = func(10 if aat else 11, sp_ref_a, scalar, float(out_scalar) if out_scalar is not None else 1., output_arr.ctypes.data_as(_ctypes.POINTER(output_ctype)), LAYOUT_CODE_C, output_ld) # Check return if ret_val != 0: _err_msg = "{fn} returned {v} ({e})".format(fn=func.__name__, v=ret_val, e=RETURN_CODES[ret_val]) raise ValueError(_err_msg) _destroy_mkl_handle(sp_ref_a) # This fixes a specific bug in mkl_sparse_d_syrkd which returns a full matrix # This stupid thing only happens with specific flags # I could probably leave it but it's pretty annoying if not aat and out is None: output_arr[np.tril_indices(output_arr.shape[0], k=-1)] = 0. return output_arr