summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorMatthew Badin <mbadin@apple.com>2021-04-28 16:56:01 -0700
committerMatthew Badin <mbadin@apple.com>2021-04-28 17:20:11 -0700
commite28da7a3d50862fa99b8b704d60fc6543b5af631 (patch)
tree57f4c3684d5333c319d3b7a3cbcdbb087418acc4 /numpy
parent53e34d49d46b0999927160444a7dd0650efafad7 (diff)
downloadnumpy-e28da7a3d50862fa99b8b704d60fc6543b5af631.tar.gz
BLD: Enable Accelerate Framework
Diffstat (limited to 'numpy')
-rw-r--r--numpy/_build_utils/README9
-rw-r--r--numpy/_build_utils/__init__.py0
-rw-r--r--numpy/_build_utils/apple_accelerate.py21
-rw-r--r--numpy/core/setup.py9
-rw-r--r--numpy/core/tests/test_multiarray.py65
-rw-r--r--numpy/distutils/system_info.py54
-rw-r--r--numpy/linalg/setup.py5
7 files changed, 118 insertions, 45 deletions
diff --git a/numpy/_build_utils/README b/numpy/_build_utils/README
new file mode 100644
index 000000000..73d93593e
--- /dev/null
+++ b/numpy/_build_utils/README
@@ -0,0 +1,9 @@
+=======
+WARNING
+=======
+
+This directory (numpy/_build_utils) is *not* part of the public numpy API,
+ - it is internal build support for numpy.
+ - it is only present in source distributions or during an in place build
+ - it is *not* installed with the rest of numpy
+
diff --git a/numpy/_build_utils/__init__.py b/numpy/_build_utils/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/numpy/_build_utils/__init__.py
diff --git a/numpy/_build_utils/apple_accelerate.py b/numpy/_build_utils/apple_accelerate.py
new file mode 100644
index 000000000..8ce54619e
--- /dev/null
+++ b/numpy/_build_utils/apple_accelerate.py
@@ -0,0 +1,21 @@
+import os
+import sys
+import re
+
+__all__ = ['uses_accelerate_framework']
+
+def uses_accelerate_framework(info):
+ """ Returns True if Accelerate framework is used for BLAS/LAPACK """
+ # If we're not building on Darwin (macOS), don't use Accelerate
+ if sys.platform != "darwin":
+ return False
+ # If we're building on macOS, but targeting a different platform,
+ # don't use Accelerate.
+ if os.getenv('_PYTHON_HOST_PLATFORM', None):
+ return False
+ r_accelerate = re.compile("Accelerate")
+ extra_link_args = info.get('extra_link_args', '')
+ for arg in extra_link_args:
+ if r_accelerate.search(arg):
+ return True
+ return False
diff --git a/numpy/core/setup.py b/numpy/core/setup.py
index df405bcaf..f59b67c88 100644
--- a/numpy/core/setup.py
+++ b/numpy/core/setup.py
@@ -10,6 +10,10 @@ from os.path import join
from numpy.distutils import log
from distutils.dep_util import newer
from sysconfig import get_config_var
+
+from numpy._build_utils.apple_accelerate import (
+ uses_accelerate_framework
+ )
from numpy.compat import npy_load_module
from setup_common import * # noqa: F403
@@ -405,11 +409,6 @@ def configuration(parent_package='',top_path=None):
from numpy.distutils.system_info import (get_info, blas_opt_info,
lapack_opt_info)
- # Accelerate is buggy, disallow it. See also numpy/linalg/setup.py
- for opt_order in (blas_opt_info.blas_order, lapack_opt_info.lapack_order):
- if 'accelerate' in opt_order:
- opt_order.remove('accelerate')
-
config = Configuration('core', parent_package, top_path)
local_dir = config.local_path
codegen_dir = join(local_dir, 'code_generators')
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
index 073433bd1..40b435f2e 100644
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -6194,6 +6194,71 @@ class TestDot:
assert_equal(np.dot(b, a), res)
assert_equal(np.dot(b, b), res)
+ def test_accelerate_framework_sgemv_fix(self):
+
+ def aligned_array(shape, align, dtype, order='C'):
+ d = dtype(0)
+ N = np.prod(shape)
+ tmp = np.zeros(N * d.nbytes + align, dtype=np.uint8)
+ address = tmp.__array_interface__["data"][0]
+ for offset in range(align):
+ if (address + offset) % align == 0:
+ break
+ tmp = tmp[offset:offset+N*d.nbytes].view(dtype=dtype)
+ return tmp.reshape(shape, order=order)
+
+ def as_aligned(arr, align, dtype, order='C'):
+ aligned = aligned_array(arr.shape, align, dtype, order)
+ aligned[:] = arr[:]
+ return aligned
+
+ def assert_dot_close(A, X, desired):
+ assert_allclose(np.dot(A, X), desired, rtol=1e-5, atol=1e-7)
+
+ m = aligned_array(100, 15, np.float32)
+ s = aligned_array((100, 100), 15, np.float32)
+ np.dot(s, m) # this will always segfault if the bug is present
+
+ testdata = itertools.product((15,32), (10000,), (200,89), ('C','F'))
+ for align, m, n, a_order in testdata:
+ # Calculation in double precision
+ A_d = np.random.rand(m, n)
+ X_d = np.random.rand(n)
+ desired = np.dot(A_d, X_d)
+ # Calculation with aligned single precision
+ A_f = as_aligned(A_d, align, np.float32, order=a_order)
+ X_f = as_aligned(X_d, align, np.float32)
+ assert_dot_close(A_f, X_f, desired)
+ # Strided A rows
+ A_d_2 = A_d[::2]
+ desired = np.dot(A_d_2, X_d)
+ A_f_2 = A_f[::2]
+ assert_dot_close(A_f_2, X_f, desired)
+ # Strided A columns, strided X vector
+ A_d_22 = A_d_2[:, ::2]
+ X_d_2 = X_d[::2]
+ desired = np.dot(A_d_22, X_d_2)
+ A_f_22 = A_f_2[:, ::2]
+ X_f_2 = X_f[::2]
+ assert_dot_close(A_f_22, X_f_2, desired)
+ # Check the strides are as expected
+ if a_order == 'F':
+ assert_equal(A_f_22.strides, (8, 8 * m))
+ else:
+ assert_equal(A_f_22.strides, (8 * n, 8))
+ assert_equal(X_f_2.strides, (8,))
+ # Strides in A rows + cols only
+ X_f_2c = as_aligned(X_f_2, align, np.float32)
+ assert_dot_close(A_f_22, X_f_2c, desired)
+ # Strides just in A cols
+ A_d_12 = A_d[:, ::2]
+ desired = np.dot(A_d_12, X_d_2)
+ A_f_12 = A_f[:, ::2]
+ assert_dot_close(A_f_12, X_f_2c, desired)
+ # Strides in A cols and X
+ assert_dot_close(A_f_12, X_f_2, desired)
+
+
class MatmulCommon:
"""Common tests for '@' operator and numpy.matmul.
diff --git a/numpy/distutils/system_info.py b/numpy/distutils/system_info.py
index 9e192329f..082b029d7 100644
--- a/numpy/distutils/system_info.py
+++ b/numpy/distutils/system_info.py
@@ -375,22 +375,6 @@ default_src_dirs = [_m for _m in default_src_dirs if os.path.isdir(_m)]
so_ext = get_shared_lib_extension()
-def is_symlink_to_accelerate(filename):
- accelpath = '/System/Library/Frameworks/Accelerate.framework'
- return (sys.platform == 'darwin' and os.path.islink(filename) and
- os.path.realpath(filename).startswith(accelpath))
-
-
-_accel_msg = (
- 'Found {filename}, but that file is a symbolic link to the '
- 'MacOS Accelerate framework, which is not supported by NumPy. '
- 'You must configure the build to use a different optimized library, '
- 'or disable the use of optimized BLAS and LAPACK by setting the '
- 'environment variables NPY_BLAS_ORDER="" and NPY_LAPACK_ORDER="" '
- 'before building NumPy.'
-)
-
-
def get_standard_file(fname):
"""Returns a list of files named 'fname' from
1) System-wide directory (directory-location of this module)
@@ -539,6 +523,7 @@ def get_info(name, notfound_action=0):
'blis': blis_info, # use blas_opt instead
'lapack_mkl': lapack_mkl_info, # use lapack_opt instead
'blas_mkl': blas_mkl_info, # use blas_opt instead
+ 'accelerate': accelerate_info, # use blas_opt instead
'openblas64_': openblas64__info,
'openblas64__lapack': openblas64__lapack_info,
'openblas_ilp64': openblas_ilp64_info,
@@ -1029,9 +1014,6 @@ class system_info:
for prefix in lib_prefixes:
p = self.combine_paths(lib_dir, prefix + lib + ext)
if p:
- # p[0] is the full path to the binary library file.
- if is_symlink_to_accelerate(p[0]):
- raise RuntimeError(_accel_msg.format(filename=p[0]))
break
if p:
assert len(p) == 1
@@ -1766,10 +1748,18 @@ def get_atlas_version(**config):
class lapack_opt_info(system_info):
notfounderror = LapackNotFoundError
+
# List of all known LAPACK libraries, in the default order
- lapack_order = ['mkl', 'openblas', 'flame', 'atlas', 'lapack']
+ lapack_order = ['accelerate', 'mkl', 'openblas', 'flame', 'atlas', 'lapack']
order_env_var_name = 'NPY_LAPACK_ORDER'
+ def _calc_info_accelerate(self):
+ info = get_info('accelerate')
+ if info:
+ self.set_info(**info)
+ return True
+ return False
+
def _calc_info_mkl(self):
info = get_info('lapack_mkl')
if info:
@@ -1820,13 +1810,6 @@ class lapack_opt_info(system_info):
return True
return False
- def _calc_info_accelerate(self):
- info = get_info('accelerate')
- if info:
- self.set_info(**info)
- return True
- return False
-
def _get_info_blas(self):
# Default to get the optimized BLAS implementation
info = get_info('blas_opt')
@@ -1942,9 +1925,17 @@ class lapack64__opt_info(lapack_ilp64_opt_info):
class blas_opt_info(system_info):
notfounderror = BlasNotFoundError
# List of all known BLAS libraries, in the default order
- blas_order = ['mkl', 'blis', 'openblas', 'atlas', 'blas']
+
+ blas_order = ['accelerate', 'mkl', 'blis', 'openblas', 'atlas', 'blas']
order_env_var_name = 'NPY_BLAS_ORDER'
+ def _calc_info_accelerate(self):
+ info = get_info('accelerate')
+ if info:
+ self.set_info(**info)
+ return True
+ return False
+
def _calc_info_mkl(self):
info = get_info('blas_mkl')
if info:
@@ -1979,13 +1970,6 @@ class blas_opt_info(system_info):
return True
return False
- def _calc_info_accelerate(self):
- info = get_info('accelerate')
- if info:
- self.set_info(**info)
- return True
- return False
-
def _calc_info_blas(self):
# Warn about a non-optimized BLAS library
warnings.warn(BlasOptNotFoundError.__doc__ or '', stacklevel=3)
diff --git a/numpy/linalg/setup.py b/numpy/linalg/setup.py
index 5c9f2a4cb..e2944f38c 100644
--- a/numpy/linalg/setup.py
+++ b/numpy/linalg/setup.py
@@ -9,11 +9,6 @@ def configuration(parent_package='', top_path=None):
config.add_subpackage('tests')
- # Accelerate is buggy, disallow it. See also numpy/core/setup.py
- for opt_order in (blas_opt_info.blas_order, lapack_opt_info.lapack_order):
- if 'accelerate' in opt_order:
- opt_order.remove('accelerate')
-
# Configure lapack_lite
src_dir = 'lapack_lite'