Skip to content

Commit

Permalink
Merge pull request #11 from flatironinstitute/bsr
Browse files Browse the repository at this point in the history
v0.7.0 (BSR)
  • Loading branch information
asistradition authored Oct 30, 2020
2 parents ffb987f + 4799fcd commit fef7448
Show file tree
Hide file tree
Showing 16 changed files with 737 additions and 246 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
### Version 0.7.0

* Added support for block sparse row (BSR) format matrices
* Added `sparse_dot_mkl.set_debug_mode(True)` as a module-level debug mode flag instead of calling debug mode from
`dot_product_mkl`. Added a number of debug messages for troubleshooting during development.

### Version 0.6.0

* Added an `out` parameter that will add a matrix multiplication to an already-allocated dense array
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ Three functions are explicitly available - `dot_product_mkl`, `gram_matrix_mkl`,
#### dot_product_mkl
`dot_product_mkl(matrix_a, matrix_b, cast=False, copy=True, reorder_output=False, dense=False, debug=False, out=None, out_scalar=None)`

`matrix_a` and `matrix_b` are either numpy arrays (1d or 2d) or scipy sparse matrices (CSR or CSC).
Sparse COO or BSR matrices are not supported.
`matrix_a` and `matrix_b` are either numpy arrays (1d or 2d) or scipy sparse matrices (CSR, CSC, or BSR).
BSR matrices are supported for matrix-matrix multiplication only if one matrix is a dense array or both sparse matrices are BSR.
Sparse COO matrices are not supported.
Numpy arrays must be contiguous. Non-contiguous arrays should be copied to a contiguous array prior to calling this
function.

Expand All @@ -42,7 +43,7 @@ Input sparse matrices may be reordered without warning in place.
This will not change data, only the way it is stored.
Scipy matrix multiplication does not produce ordered outputs, so this defaults to `False`.

`out` is an optional reference to an output array to which the product of the matrix multiplication will be added.
`out` is an optional reference to a dense output array to which the product of the matrix multiplication will be added.
This must be identical in attributes to the array that would be returned if it was not used.
Specifically it must have the correct shape, dtype, and column- or row-major order and it must be contiguous. A ValueError will be raised if any attribute of this array is incorrect.
This function will return a reference to the same array object when `out` is set.
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from setuptools import setup, find_packages

DISTNAME = 'sparse_dot_mkl'
VERSION = '0.6.0'
VERSION = '0.7.0'
DESCRIPTION = "Intel MKL wrapper for sparse matrix multiplication"
MAINTAINER = 'Chris Jackson'
MAINTAINER_EMAIL = '[email protected]'
Expand Down
3 changes: 2 additions & 1 deletion sparse_dot_mkl/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from sparse_dot_mkl.sparse_dot import dot_product_mkl, dot_product_transpose_mkl, get_version_string, gram_matrix_mkl, sparse_qr_solve_mkl
from sparse_dot_mkl.sparse_dot import (dot_product_mkl, dot_product_transpose_mkl, get_version_string, gram_matrix_mkl,
sparse_qr_solve_mkl, set_debug_mode)
8 changes: 4 additions & 4 deletions sparse_dot_mkl/_dense_dense.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from sparse_dot_mkl._mkl_interface import (MKL, _type_check, _sanity_check, _empty_output_check, _get_numpy_layout,
LAYOUT_CODE_C, LAYOUT_CODE_F, _out_matrix)
LAYOUT_CODE_C, LAYOUT_CODE_F, _out_matrix, debug_print)

import numpy as np
import ctypes as _ctypes
Expand Down Expand Up @@ -50,17 +50,17 @@ def _dense_matmul(matrix_a, matrix_b, double_precision, scalar=1., out=None, out
return output_arr.ravel() if flatten_output else output_arr


def _dense_dot_dense(matrix_a, matrix_b, cast=False, dprint=print, scalar=1., out=None, out_scalar=None):
def _dense_dot_dense(matrix_a, matrix_b, cast=False, scalar=1., out=None, out_scalar=None):

_sanity_check(matrix_a, matrix_b, allow_vector=True)

# 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")
debug_print("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 _out_matrix((matrix_a.shape[0], matrix_b.shape[1]), final_dtype, out_arr=out)

matrix_a, matrix_b = _type_check(matrix_a, matrix_b, cast=cast, dprint=dprint)
matrix_a, matrix_b = _type_check(matrix_a, matrix_b, cast=cast)

a_dbl, b_dbl = matrix_a.dtype == np.float64, matrix_b.dtype == np.float64

Expand Down
21 changes: 7 additions & 14 deletions sparse_dot_mkl/_gram_matrix.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from sparse_dot_mkl._mkl_interface import (MKL, sparse_matrix_t, RETURN_CODES, _create_mkl_sparse,
from sparse_dot_mkl._mkl_interface import (MKL, sparse_matrix_t, _create_mkl_sparse,
_export_mkl, _order_mkl_handle, _destroy_mkl_handle, _type_check,
_get_numpy_layout, _convert_to_csr, _empty_output_check, LAYOUT_CODE_C,
_out_matrix)
_out_matrix, _check_return_value, debug_print)

import scipy.sparse as _sps
import ctypes as _ctypes
Expand Down Expand Up @@ -36,11 +36,7 @@ def _gram_matrix_sparse(matrix_a, aat=False, reorder_output=False):
_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)
_check_return_value(ret_val, "mkl_sparse_syrk")

if reorder_output:
_order_mkl_handle(ref_handle)
Expand Down Expand Up @@ -95,9 +91,7 @@ def _gram_matrix_sparse_to_dense(matrix_a, aat=False, scalar=1., out=None, out_s
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)
_check_return_value(ret_val, func.__name__)

_destroy_mkl_handle(sp_ref_a)

Expand Down Expand Up @@ -157,8 +151,7 @@ def _gram_matrix_dense_to_dense(matrix_a, aat=False, scalar=1., out=None, out_sc
return output_arr


def _gram_matrix(matrix, transpose=False, cast=False, dense=False, reorder_output=False, dprint=print, out=None,
out_scalar=None):
def _gram_matrix(matrix, transpose=False, cast=False, dense=False, reorder_output=False, out=None, out_scalar=None):
"""
Calculate a gram matrix (AT (dot) A) from a sparse matrix.
Expand All @@ -180,12 +173,12 @@ def _gram_matrix(matrix, transpose=False, cast=False, dense=False, reorder_outpu

# Check for edge condition inputs which result in empty outputs
if _empty_output_check(matrix, matrix):
dprint("Skipping multiplication because AT (dot) A must yield an empty matrix")
debug_print("Skipping multiplication because AT (dot) A must yield an empty matrix")
output_shape = (matrix.shape[1], matrix.shape[1]) if transpose else (matrix.shape[0], matrix.shape[0])
output_func = _sps.csr_matrix if _sps.isspmatrix(matrix) else np.zeros
return output_func(output_shape, dtype=matrix.dtype)

matrix = _type_check(matrix, cast=cast, dprint=dprint)
matrix = _type_check(matrix, cast=cast)

if _sps.isspmatrix(matrix) and not (_sps.isspmatrix_csr(matrix) or _sps.isspmatrix_csc(matrix)):
raise ValueError("gram_matrix requires sparse matrix to be CSR or CSC format")
Expand Down
Loading

0 comments on commit fef7448

Please sign in to comment.