├── afnumpy ├── linalg │ ├── __init__.py │ └── linalg.py ├── core │ ├── __init__.py │ ├── function_base.py │ ├── shape_base.py │ ├── multiarray.py │ ├── dtypes.py │ ├── numeric.py │ └── fromnumeric.py ├── lib │ ├── __init__.py │ ├── type_check.py │ ├── shape_base.py │ ├── stride_tricks.py │ ├── function_base.py │ ├── scimath.py │ └── arraypad.py ├── random.py ├── sparse │ ├── base.py │ ├── sputils.py │ ├── csr.py │ └── __init__.py ├── decorators.py ├── fft.py ├── __init__.py ├── private_utils.py ├── indexing.py └── multiarray.py ├── setup.cfg ├── tests ├── conftest.py ├── test_arrayfire.py ├── test_performance.py ├── asserts.py ├── test_jit.py ├── test_linalg.py ├── test_fft.py ├── test_core.py ├── test_lib.py └── test_ndarray.py ├── README.md ├── LICENSE ├── .gitlab-ci.yml ├── .travis.yml ├── setup.py └── examples └── phase_retrieval.py /afnumpy/linalg/__init__.py: -------------------------------------------------------------------------------- 1 | from .linalg import * 2 | -------------------------------------------------------------------------------- /afnumpy/core/__init__.py: -------------------------------------------------------------------------------- 1 | from .dtypes import * 2 | from .numeric import * 3 | from .fromnumeric import * 4 | from .shape_base import * 5 | from .function_base import * 6 | from .multiarray import * 7 | -------------------------------------------------------------------------------- /afnumpy/lib/__init__.py: -------------------------------------------------------------------------------- 1 | from .function_base import * 2 | from .stride_tricks import * 3 | from .shape_base import * 4 | from .scimath import * 5 | from .type_check import * 6 | from .arraypad import pad 7 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md 3 | 4 | [aliases] 5 | test=pytest 6 | 7 | [tool:pytest] 8 | addopts = -s -v --benchmark-compare --benchmark-autosave --benchmark-group-by=fullname 9 | python_files = tests/*.py 10 | -------------------------------------------------------------------------------- /afnumpy/random.py: -------------------------------------------------------------------------------- 1 | import numpy.random 2 | from . import ndarray 3 | 4 | def rand(*args): 5 | a = numpy.random.rand(*args) 6 | return ndarray(a.shape, a.dtype, buffer=a) 7 | 8 | 9 | def random(size = None): 10 | a = numpy.random.random(size) 11 | return ndarray(a.shape, a.dtype, buffer=a) 12 | -------------------------------------------------------------------------------- /afnumpy/lib/type_check.py: -------------------------------------------------------------------------------- 1 | import numpy.core.numeric as _nx 2 | from numpy.core.numeric import obj2sctype 3 | import afnumpy 4 | 5 | def asfarray(a, dtype=_nx.float_): 6 | dtype = _nx.obj2sctype(dtype) 7 | if not issubclass(dtype, _nx.inexact): 8 | dtype = _nx.float_ 9 | return afnumpy.asarray(a, dtype=dtype) 10 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import arrayfire 2 | import pytest 3 | 4 | backends = arrayfire.library.get_available_backends() 5 | # do not use opencl backend, it's kinda broken 6 | #backends = [x for x in backends if x != 'opencl'] 7 | 8 | # This will set the different backends before each test is executed 9 | @pytest.fixture(scope="function", params=backends, autouse=True) 10 | def set_backend(request): 11 | arrayfire.library.set_backend(request.param, unsafe=True) 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # afnumpy 2 | Aims to be a GPU-ready drop-in replacement for numpy. Uses [ArrayFire](http://github.com/arrayfire/arrayfire) to do the actual computing. 3 | 4 | [![Build Status](https://travis-ci.org/FilipeMaia/afnumpy.svg)](https://travis-ci.org/FilipeMaia/afnumpy) [![codecov.io](https://codecov.io/github/FilipeMaia/afnumpy/coverage.svg?branch=master)](https://codecov.io/github/FilipeMaia/afnumpy?branch=master) 5 | [![DOI](https://zenodo.org/badge/35001963.svg)](https://zenodo.org/badge/latestdoi/35001963) 6 | 7 | 8 | ### How to cite 9 | Please cite afnumpy as: 10 | 11 | Filipe R. N. C. Maia. (2017). afnumpy: A GPU-ready drop-in replacement for NumPy, http://doi.org/10.5281/zenodo.262201 12 | -------------------------------------------------------------------------------- /tests/test_arrayfire.py: -------------------------------------------------------------------------------- 1 | import arrayfire 2 | 3 | # We're going to test several arrayfire behaviours that we rely on 4 | 5 | 6 | from asserts import * 7 | import afnumpy as af 8 | import numpy as np 9 | 10 | def test_af_cast(): 11 | a = afnumpy.arrayfire.randu(2,3) 12 | # Check that device_ptr does not cause a copy 13 | assert a.device_ptr() == a.device_ptr() 14 | # Check that cast does not cause a copy 15 | assert arrayfire.cast(a, a.dtype()).device_ptr() == a.device_ptr() 16 | 17 | def test_cast(): 18 | a = afnumpy.random.rand(2,3) 19 | # Check that device_ptr does not cause a copy 20 | assert a.d_array.device_ptr() == a.d_array.device_ptr() 21 | # Check that cast does not cause a copy 22 | assert arrayfire.cast(a.d_array, a.d_array.dtype()).device_ptr() == a.d_array.device_ptr() 23 | -------------------------------------------------------------------------------- /afnumpy/core/function_base.py: -------------------------------------------------------------------------------- 1 | import afnumpy 2 | from numpy import result_type 3 | 4 | def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None): 5 | num = int(num) 6 | 7 | # Convert float/complex array scalars to float, gh-3504 8 | start = start * 1. 9 | stop = stop * 1. 10 | 11 | if dtype is None: 12 | dtype = result_type(start, stop, float(num)) 13 | 14 | if num <= 0: 15 | return array([], dtype) 16 | if endpoint: 17 | if num == 1: 18 | return array([start], dtype=dtype) 19 | step = (stop-start)/float((num-1)) 20 | y = afnumpy.arange(0, num, dtype=dtype) * step + start 21 | y[-1] = stop 22 | else: 23 | step = (stop-start)/float(num) 24 | y = afnumpy.arange(0, num, dtype=dtype) * step + start 25 | if retstep: 26 | return y.astype(dtype), step 27 | else: 28 | return y.astype(dtype) 29 | -------------------------------------------------------------------------------- /afnumpy/lib/shape_base.py: -------------------------------------------------------------------------------- 1 | import afnumpy 2 | import arrayfire 3 | import numpy 4 | from .. import private_utils as pu 5 | 6 | def tile(A, reps): 7 | try: 8 | tup = tuple(reps) 9 | except TypeError: 10 | tup = (reps,) 11 | if(len(tup) > 4): 12 | raise NotImplementedError('Only up to 4 dimensions are supported') 13 | 14 | d = len(tup) 15 | shape = list(A.shape) 16 | n = max(A.size, 1) 17 | if (d < A.ndim): 18 | tup = (1,)*(A.ndim-d) + tup 19 | 20 | # Calculate final shape 21 | while len(shape) < len(tup): 22 | shape.insert(0,1) 23 | shape = tuple(numpy.array(shape) * numpy.array(tup)) 24 | 25 | # Prepend ones to simplify calling 26 | if (d < 4): 27 | tup = (1,)*(4-d) + tup 28 | tup = pu.c2f(tup) 29 | s = arrayfire.tile(A.d_array, int(tup[0]), int(tup[1]), int(tup[2]), int(tup[3])) 30 | return afnumpy.ndarray(shape, dtype=pu.typemap(s.dtype()), af_array=s) 31 | 32 | 33 | -------------------------------------------------------------------------------- /tests/test_performance.py: -------------------------------------------------------------------------------- 1 | import afnumpy 2 | import arrayfire 3 | import numpy 4 | 5 | def test_fft2(benchmark): 6 | a = afnumpy.ones((256,256),dtype=numpy.complex64) 7 | benchmark(afnumpy.fft.fftn, a) 8 | 9 | def test_ifft2(benchmark): 10 | a = afnumpy.ones((256,256),dtype=numpy.complex64) 11 | benchmark(afnumpy.fft.ifftn, a) 12 | 13 | def test_fftshift(benchmark): 14 | a = afnumpy.ones((256,256),dtype=numpy.complex64) 15 | benchmark(afnumpy.fft.fftshift, a) 16 | 17 | def test_ifftshift(benchmark): 18 | a = afnumpy.ones((256,256),dtype=numpy.complex64) 19 | benchmark(afnumpy.fft.ifftshift, a) 20 | 21 | def test_array_nocopy(benchmark): 22 | a = afnumpy.ones((2048,2048),dtype=numpy.complex64) 23 | benchmark(afnumpy.array, a, copy=False) 24 | 25 | def test_array_copy(benchmark): 26 | a = afnumpy.ones((2048,2048),dtype=numpy.complex64) 27 | benchmark(afnumpy.array, a, copy=True) 28 | 29 | def test_add(benchmark): 30 | a = afnumpy.ones((2048,2048),dtype=numpy.complex64) 31 | b = afnumpy.ones((2048,2048),dtype=numpy.complex64) 32 | benchmark(afnumpy.add, a, b) 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /afnumpy/sparse/base.py: -------------------------------------------------------------------------------- 1 | class spmatrix(object): 2 | """ This class provides a base class for all sparse matrices. It 3 | cannot be instantiated. Most of the work is provided by subclasses. 4 | """ 5 | 6 | __array_priority__ = 10.1 7 | ndim = 2 8 | 9 | def __init__(self, maxprint=100): 10 | self._shape = None 11 | if self.__class__.__name__ == 'spmatrix': 12 | raise ValueError("This class is not intended" 13 | " to be instantiated directly.") 14 | self.maxprint = maxprint 15 | 16 | 17 | def isspmatrix(x): 18 | """Is x of a sparse matrix type? 19 | 20 | Parameters 21 | ---------- 22 | x 23 | object to check for being a sparse matrix 24 | 25 | Returns 26 | ------- 27 | bool 28 | True if x is a sparse matrix, False otherwise 29 | 30 | Notes 31 | ----- 32 | issparse and isspmatrix are aliases for the same function. 33 | 34 | Examples 35 | -------- 36 | >>> from scipy.sparse import csr_matrix, isspmatrix 37 | >>> isspmatrix(csr_matrix([[5]])) 38 | True 39 | 40 | >>> from scipy.sparse import isspmatrix 41 | >>> isspmatrix(5) 42 | False 43 | """ 44 | return isinstance(x, spmatrix) -------------------------------------------------------------------------------- /afnumpy/core/shape_base.py: -------------------------------------------------------------------------------- 1 | from . import numeric as _nx 2 | from .numeric import asanyarray, newaxis 3 | 4 | def atleast_1d(*arys): 5 | res = [] 6 | for ary in arys: 7 | ary = asanyarray(ary) 8 | if len(ary.shape) == 0 : 9 | result = ary.reshape(1) 10 | else : 11 | result = ary 12 | res.append(result) 13 | if len(res) == 1: 14 | return res[0] 15 | else: 16 | return res 17 | 18 | def atleast_2d(*arys): 19 | res = [] 20 | for ary in arys: 21 | ary = asanyarray(ary) 22 | if len(ary.shape) == 0 : 23 | result = ary.reshape(1, 1) 24 | elif len(ary.shape) == 1 : 25 | result = ary[newaxis,:] 26 | else : 27 | result = ary 28 | res.append(result) 29 | if len(res) == 1: 30 | return res[0] 31 | else: 32 | return res 33 | 34 | def vstack(tup): 35 | return _nx.concatenate([atleast_2d(_m) for _m in tup], 0) 36 | 37 | 38 | def hstack(tup): 39 | arrs = [atleast_1d(_m) for _m in tup] 40 | # As a special case, dimension 0 of 1-dimensional arrays is "horizontal" 41 | if arrs[0].ndim == 1: 42 | return _nx.concatenate(arrs, 0) 43 | else: 44 | return _nx.concatenate(arrs, 1) 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Filipe Maia 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /afnumpy/sparse/sputils.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | 4 | def isintlike(x): 5 | """Is x appropriate as an index into a sparse matrix? Returns True 6 | if it can be cast safely to a machine int. 7 | """ 8 | # Fast-path check to eliminate non-scalar values. operator.index would 9 | # catch this case too, but the exception catching is slow. 10 | if np.ndim(x) != 0: 11 | return False 12 | try: 13 | operator.index(x) 14 | except (TypeError, ValueError): 15 | try: 16 | loose_int = bool(int(x) == x) 17 | except (TypeError, ValueError): 18 | return False 19 | if loose_int: 20 | warnings.warn("Inexact indices into sparse matrices are deprecated", 21 | DeprecationWarning) 22 | return loose_int 23 | return True 24 | 25 | def isshape(x, nonneg=False): 26 | """Is x a valid 2-tuple of dimensions? 27 | 28 | If nonneg, also checks that the dimensions are non-negative. 29 | """ 30 | try: 31 | # Assume it's a tuple of matrix dimensions (M, N) 32 | (M, N) = x 33 | except: 34 | return False 35 | else: 36 | if isintlike(M) and isintlike(N): 37 | if np.ndim(M) == 0 and np.ndim(N) == 0: 38 | if not nonneg or (M >= 0 and N >= 0): 39 | return True 40 | return False -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | stages: 2 | - prepare 3 | - build 4 | - test 5 | 6 | prepare_job: 7 | tags: 8 | - cuda 9 | stage: prepare 10 | script: 11 | - cd $HOME 12 | - if [ ! -d "$HOME/arrayfire/.git" ]; then git clone https://github.com/arrayfire/arrayfire; else echo 'Using arrayfire from cached directory'; fi 13 | - mkdir -p arrayfire/build && cd arrayfire 14 | - git pull 15 | - git submodule init 16 | - git submodule update 17 | - cd build 18 | - cmake -DCMAKE_CXX_COMPILER=/usr/bin/g++ -DBUILD_CPU=OFF -DBUILD_CUDA=ON -DBUILD_OPENCL=OFF -DBUILD_GRAPHICS=OFF -DBUILD_TEST=OFF -DBUILD_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX=${HOME}/local .. 19 | - make -j 20 | - make install 21 | # Install arrayfire-python 22 | - cd $HOME 23 | - if [ ! -d "$HOME/arrayfire-python/.git" ]; then git clone https://github.com/arrayfire/arrayfire-python.git; else echo 'Using arrayfire-python from cached directory'; fi 24 | - cd arrayfire-python 25 | - git pull 26 | - python setup.py install --user 27 | 28 | build_job: 29 | tags: 30 | - cuda 31 | stage: build 32 | script: 33 | - python setup.py install --user 34 | 35 | test_job: 36 | tags: 37 | - cuda 38 | stage: test 39 | script: 40 | - export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${HOME}/local/lib 41 | - coverage run --source afnumpy -m py.test -v --color=yes --showlocals --durations=10 42 | - codecov --token=`cat ${HOME}/afnumpy_codecov.token` 43 | -------------------------------------------------------------------------------- /tests/asserts.py: -------------------------------------------------------------------------------- 1 | import afnumpy 2 | import numpy 3 | from numpy.testing import assert_allclose 4 | import numbers 5 | 6 | def massert(af_a, np_a): 7 | # Assert the metadata of the arrays 8 | 9 | if isinstance(af_a, tuple): 10 | assert(af_a == np_a) 11 | elif isinstance(af_a, afnumpy.ndarray): 12 | assert isinstance(np_a, numpy.ndarray) 13 | # I will not strictly enforce float32 vs float64 14 | # I will not strictly enforce uint32 vs int64 15 | assert (af_a.dtype == np_a.dtype or (af_a.dtype == numpy.float32 and np_a.dtype == numpy.float64) or 16 | (af_a.dtype == numpy.uint32 and np_a.dtype == numpy.int64) or 17 | (af_a.dtype == numpy.int64 and np_a.dtype == numpy.uint32) or 18 | (af_a.dtype == numpy.int32 and np_a.dtype == numpy.int64)) 19 | assert (af_a.shape == np_a.shape) 20 | elif isinstance(af_a, numbers.Number): 21 | assert isinstance(af_a, numbers.Number) 22 | assert isinstance(np_a, numbers.Number) 23 | elif isinstance(af_a, numpy.number): 24 | assert isinstance(af_a, numpy.number) 25 | assert isinstance(np_a, numpy.number) 26 | else: 27 | assert type(af_a) == type(np_a) 28 | 29 | def iassert(af_a, np_a): 30 | if not isinstance(af_a, tuple) and not isinstance(af_a, list): 31 | af_a = (af_a,) 32 | np_a = (np_a,) 33 | for a,b in zip(af_a,np_a): 34 | assert numpy.all(numpy.array(a) == b) 35 | massert(a, b) 36 | 37 | def fassert(af_a, np_a): 38 | numpy.testing.assert_allclose(numpy.array(af_a), np_a) 39 | massert(af_a, np_a) 40 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | os: linux 3 | dist: bionic 4 | 5 | python: 6 | - "2.7" 7 | - "3.6" 8 | 9 | notifications: 10 | email: false 11 | 12 | sudo: false 13 | 14 | cache: 15 | directories: 16 | - $HOME/.cache/pip 17 | - $HOME/arrayfire 18 | - $HOME/arrayfire-python 19 | - $HOME/local 20 | 21 | addons: 22 | apt: 23 | # sources: 24 | # - ubuntu-toolchain-r-test 25 | # - kubuntu-backports 26 | packages: 27 | - libatlas-base-dev 28 | - libfftw3-dev 29 | - gcc 30 | - g++ 31 | - cmake 32 | - gdb 33 | - apport 34 | - libboost-all-dev 35 | 36 | before_install: 37 | - pip install codecov 38 | - cd $HOME 39 | - if [ ! -d "$HOME/arrayfire/.git" ]; then git clone --recursive https://github.com/arrayfire/arrayfire; else echo 'Using arrayfire from cached directory'; fi 40 | - mkdir -p arrayfire/build && cd arrayfire/build 41 | - git pull 42 | - git submodule init 43 | - git submodule update 44 | - cmake -DAF_BUILD_CPU=ON -DAF_BUILD_CUDA=OFF -DAF_WITH_GRAPHICS=OFF -DAF_BUILD_OPENCL=OFF -DBUILD_TESTING=OFF -DAF_BUILD_EXAMPLES=OFF -DAF_BUILD_UNIFIED=ON -DCMAKE_INSTALL_PREFIX=${HOME}/local .. 45 | - make -j 2 46 | - make install 47 | - export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${HOME}/local/lib 48 | # Install arrayfire-python 49 | - cd $HOME 50 | - if [ ! -d "$HOME/arrayfire-python/.git" ]; then git clone https://github.com/arrayfire/arrayfire-python.git; else echo 'Using arrayfire-python from cached directory'; fi 51 | - cd arrayfire-python 52 | - git pull 53 | - python setup.py install 54 | 55 | install: 56 | - pip install numpy 57 | - pip install ipython 58 | - pip install pytest-cov 59 | - pip install pytest-benchmark 60 | - cd ${HOME}/build/FilipeMaia/afnumpy 61 | - python setup.py install 62 | 63 | after_success: 64 | - codecov 65 | 66 | before_script: 67 | # Set the core file limit to unlimited so a core file is generated upon crash 68 | - ulimit -c unlimited -S 69 | - ulimit -c 70 | 71 | script: 72 | - cd ${HOME}/build/FilipeMaia/afnumpy/tests 73 | - coverage run --source afnumpy -m py.test --benchmark-skip -v --color=yes --showlocals --durations=10 74 | - python -m pytest -v --benchmark-only --benchmark-compare --benchmark-autosave --benchmark-group-by=fullname 75 | - for i in $(find ./ -maxdepth 1 -name 'core*' -print); do gdb python core* -ex "thread apply all bt" -ex "set pagination 0" -batch; done; 76 | 77 | -------------------------------------------------------------------------------- /tests/test_jit.py: -------------------------------------------------------------------------------- 1 | import afnumpy 2 | from asserts import * 3 | import pytest 4 | xfail = pytest.mark.xfail 5 | 6 | def test_comparisons(): 7 | a = afnumpy.arange(10, dtype="float32") - 5. 8 | b = afnumpy.ones((10), dtype="float32") 9 | a.eval() 10 | a_mask = a < 0. 11 | a_sum = a_mask.sum() 12 | a -= b 13 | assert(a_sum == a_mask.sum()) 14 | a_mask = a > 0. 15 | a_sum = a_mask.sum() 16 | a -= b 17 | assert(a_sum == a_mask.sum()) 18 | a_mask = a >= 0. 19 | a_sum = a_mask.sum() 20 | a -= b 21 | assert(a_sum == a_mask.sum()) 22 | a_mask = a <= 0. 23 | a_sum = a_mask.sum() 24 | a -= b 25 | assert(a_sum == a_mask.sum()) 26 | a_mask = a == 0. 27 | a_sum = a_mask.sum() 28 | a *= 0 29 | assert(a_sum == a_mask.sum()) 30 | a = afnumpy.arange(10, dtype="float32") - 5. 31 | a.eval() 32 | a_mask = a != 0. 33 | a_sum = a_mask.sum() 34 | a *= 0 35 | assert(a_sum == a_mask.sum()) 36 | 37 | def test_unary(): 38 | a = afnumpy.arange(10, dtype="float32")-5 39 | a.eval() 40 | c = -a 41 | c_sum = c.sum() 42 | a *= -1 43 | assert(c_sum == c.sum()) 44 | c = +a 45 | c_sum = c.sum() 46 | a *= -1 47 | assert(c_sum == c.sum()) 48 | 49 | def test_arithmetic(): 50 | a = afnumpy.arange(10, dtype="float32") 51 | a.eval() 52 | c = a % 3 53 | c_sum = c.sum() 54 | a += 1 55 | assert(c_sum == c.sum()) 56 | c = 3 % a 57 | c_sum = c.sum() 58 | a += 1 59 | assert(c_sum == c.sum()) 60 | c = a**1 61 | c_sum = c.sum() 62 | a -= 1 63 | assert(c_sum == c.sum()) 64 | c = 1.02**a 65 | c_sum = c.sum() 66 | a += 1 67 | assert(c_sum == c.sum()) 68 | c = a+1 69 | c_sum = c.sum() 70 | a -= 1 71 | assert(c_sum == c.sum()) 72 | c = 1+a 73 | c_sum = c.sum() 74 | a += 1 75 | assert(c_sum == c.sum()) 76 | c = a-1 77 | c_sum = c.sum() 78 | a -= 1 79 | assert(c_sum == c.sum()) 80 | c = 1-a 81 | c_sum = c.sum() 82 | a += 1 83 | assert(c_sum == c.sum()) 84 | c = a*2 85 | c_sum = c.sum() 86 | a -= 1 87 | assert(c_sum == c.sum()) 88 | c = 2*a 89 | c_sum = c.sum() 90 | a += 1 91 | assert(c_sum == c.sum()) 92 | c = a/2 93 | c_sum = c.sum() 94 | a -= 1 95 | assert(c_sum == c.sum()) 96 | c = 2/a 97 | c_sum = c.sum() 98 | a += 1 99 | assert(c_sum == c.sum()) 100 | 101 | def test_cast(): 102 | a = afnumpy.zeros(25).astype(numpy.complex64) 103 | a[0] = 1. + 0.j 104 | assert(a[0] == 1. + 0j) 105 | -------------------------------------------------------------------------------- /tests/test_linalg.py: -------------------------------------------------------------------------------- 1 | import afnumpy 2 | import numpy 3 | from asserts import * 4 | import pytest 5 | xfail = pytest.mark.xfail 6 | 7 | def test_norm(): 8 | a = afnumpy.random.random((3)) 9 | b = numpy.array(a) 10 | fassert(afnumpy.linalg.norm(a), numpy.linalg.norm(b)) 11 | fassert(afnumpy.linalg.norm(a, ord=0), numpy.linalg.norm(b, ord=0)) 12 | fassert(afnumpy.linalg.norm(a, ord=1), numpy.linalg.norm(b, ord=1)) 13 | fassert(afnumpy.linalg.norm(a, ord=-1), numpy.linalg.norm(b, ord=-1)) 14 | fassert(afnumpy.linalg.norm(a, ord=2), numpy.linalg.norm(b, ord=2)) 15 | fassert(afnumpy.linalg.norm(a, ord=-2), numpy.linalg.norm(b, ord=-2)) 16 | fassert(afnumpy.linalg.norm(a, ord=3), numpy.linalg.norm(b, ord=3)) 17 | fassert(afnumpy.linalg.norm(a, ord=numpy.Inf), numpy.linalg.norm(b, ord=numpy.Inf)) 18 | fassert(afnumpy.linalg.norm(a, ord=-numpy.Inf), numpy.linalg.norm(b, ord=-numpy.Inf)) 19 | 20 | def test_vdot(): 21 | b = numpy.random.random(3)+numpy.random.random(3)*1.0j 22 | a = afnumpy.array(b) 23 | fassert(afnumpy.vdot(a,a), numpy.vdot(b,b)) 24 | b = numpy.random.random((3,3))+numpy.random.random((3,3))*1.0j 25 | a = afnumpy.array(b) 26 | fassert(afnumpy.vdot(a,a), numpy.vdot(b,b)) 27 | b = numpy.random.random((3,3,3))+numpy.random.random((3,3,3))*1.0j 28 | a = afnumpy.array(b) 29 | fassert(afnumpy.vdot(a,a), numpy.vdot(b,b)) 30 | 31 | def test_dot_1D(): 32 | b = numpy.random.random(3)+numpy.random.random(3)*1.0j 33 | a = afnumpy.array(b) 34 | fassert(afnumpy.dot(a,a), numpy.dot(b,b)) 35 | 36 | a = numpy.random.random(3)+numpy.random.random(3)*1.0j 37 | b = numpy.random.random(3) 38 | fassert(afnumpy.dot(afnumpy.array(a),afnumpy.array(b)), numpy.dot(a,b)) 39 | 40 | def test_dot_2D(): 41 | b = numpy.random.random((3,3))+numpy.random.random((3,3))*1.0j 42 | a = afnumpy.array(b) 43 | fassert(afnumpy.dot(a,a), numpy.dot(b,b)) 44 | 45 | a = numpy.random.random((3,3))+numpy.random.random((3,3))*1.0j 46 | b = numpy.random.random((3,3)) 47 | fassert(afnumpy.dot(afnumpy.array(a),afnumpy.array(b)), numpy.dot(a,b)) 48 | out = afnumpy.array(a) 49 | fassert(afnumpy.dot(afnumpy.array(a),afnumpy.array(b),out=out), numpy.dot(a,b)) 50 | 51 | def test_dot_3D(): 52 | b = numpy.random.random((3,3,3))+numpy.random.random((3,3,3))*1.0j 53 | a = afnumpy.array(b) 54 | fassert(afnumpy.dot(a,a), numpy.dot(b,b)) 55 | 56 | a = numpy.random.random((3,2,4))+numpy.random.random((3,2,4))*1.0j 57 | b = numpy.random.random((3,4,1)) 58 | fassert(afnumpy.dot(afnumpy.array(a),afnumpy.array(b)), numpy.dot(a,b)) 59 | 60 | b = numpy.random.random((3,3,3)) 61 | a = numpy.random.random((3,3)) 62 | fassert(afnumpy.dot(afnumpy.array(a),afnumpy.array(b)), numpy.dot(a,b)) 63 | 64 | -------------------------------------------------------------------------------- /afnumpy/lib/stride_tricks.py: -------------------------------------------------------------------------------- 1 | import afnumpy 2 | import numpy 3 | 4 | def broadcast_arrays(*args, **kwargs): 5 | subok = kwargs.pop('subok', False) 6 | if kwargs: 7 | raise TypeError('broadcast_arrays() got an unexpected keyword ' 8 | 'argument {}'.format(kwargs.pop())) 9 | args = [afnumpy.array(_m, copy=False, subok=subok) for _m in args] 10 | shapes = [x.shape for x in args] 11 | if len(set(shapes)) == 1: 12 | # Common case where nothing needs to be broadcasted. 13 | return args 14 | shapes = [list(s) for s in shapes] 15 | strides = [list(x.strides) for x in args] 16 | nds = [len(s) for s in shapes] 17 | biggest = max(nds) 18 | # Go through each array and prepend dimensions of length 1 to each of 19 | # the shapes in order to make the number of dimensions equal. 20 | for i in range(len(args)): 21 | diff = biggest - nds[i] 22 | if diff > 0: 23 | shapes[i] = [1] * diff + shapes[i] 24 | strides[i] = [0] * diff + strides[i] 25 | # Chech each dimension for compatibility. A dimension length of 1 is 26 | # accepted as compatible with any other length. 27 | common_shape = [] 28 | for axis in range(biggest): 29 | lengths = [s[axis] for s in shapes] 30 | unique = set(lengths + [1]) 31 | if len(unique) > 2: 32 | # There must be at least two non-1 lengths for this axis. 33 | raise ValueError("shape mismatch: two or more arrays have " 34 | "incompatible dimensions on axis %r." % (axis,)) 35 | elif len(unique) == 2: 36 | # There is exactly one non-1 length. The common shape will take 37 | # this value. 38 | unique.remove(1) 39 | new_length = unique.pop() 40 | common_shape.append(new_length) 41 | # For each array, if this axis is being broadcasted from a 42 | # length of 1, then set its stride to 0 so that it repeats its 43 | # data. 44 | for i in range(len(args)): 45 | if shapes[i][axis] == 1: 46 | shapes[i][axis] = new_length 47 | strides[i][axis] = 0 48 | else: 49 | # Every array has a length of 1 on this axis. Strides can be 50 | # left alone as nothing is broadcasted. 51 | common_shape.append(1) 52 | 53 | # Construct the new arrays. 54 | broadcasted = [] 55 | 56 | for (x, sh) in zip(args, shapes): 57 | x_sh = x.shape + (1,)*(len(sh)-x.ndim) 58 | reps = numpy.array(sh)//numpy.array(x_sh) 59 | if(numpy.prod(reps) > 1): 60 | broadcasted.append(afnumpy.tile(x, reps)) 61 | else: 62 | if(x.shape != tuple(sh)): 63 | x = x.reshape(sh) 64 | broadcasted.append(x) 65 | 66 | 67 | return broadcasted 68 | 69 | -------------------------------------------------------------------------------- /afnumpy/decorators.py: -------------------------------------------------------------------------------- 1 | import afnumpy 2 | from . import private_utils as pu 3 | 4 | def outufunc(func): 5 | # This could use some optimization 6 | def wrapper(*args, **kws): 7 | out = kws.pop('out', None) 8 | ret = func(*args, **kws) 9 | if out is not None: 10 | if(out.ndim): 11 | out[:] = ret 12 | else: 13 | out[()] = ret 14 | return out 15 | return ret 16 | return wrapper 17 | 18 | def iufunc(func): 19 | def wrapper(*args, **kws): 20 | if all(isinstance(A, afnumpy.ndarray) for A in args): 21 | bcast_args = afnumpy.broadcast_arrays(*args) 22 | if(bcast_args[0].shape is not args[0].shape): 23 | raise ValueError("non-broadcastable output operand with" 24 | " shape %s doesn't match the broadcast" 25 | " shape %s" % (args[0].shape, bcast_args[0].shape)) 26 | args = bcast_args 27 | 28 | ret = func(*args, **kws) 29 | if len(ret.shape) == 0: 30 | return ret[()] 31 | return ret 32 | return wrapper 33 | 34 | def ufunc(func): 35 | def wrapper(*args, **kws): 36 | if all(isinstance(A, afnumpy.ndarray) for A in args): 37 | args = afnumpy.broadcast_arrays(*args) 38 | ret = func(*args, **kws) 39 | if len(ret.shape) == 0: 40 | return ret[()] 41 | return ret 42 | return wrapper 43 | 44 | def reductufunc(func): 45 | def wrapper(a, axis=None, dtype=None, keepdims=False): 46 | if not isinstance(axis, tuple): 47 | axis = (axis,) 48 | if axis[0] is None: 49 | # Special case for speed 50 | a_flat = a.flat 51 | s = func(a_flat, a_flat.d_array, pu.c2f(a_flat.shape, 0)) 52 | if keepdims: 53 | shape = (1,)*a.ndim 54 | else: 55 | shape = () 56 | ret = afnumpy.ndarray(tuple(shape), dtype=pu.typemap(s.dtype()), 57 | af_array=s) 58 | else: 59 | shape = list(a.shape) 60 | s = a.d_array 61 | # Do in decreasing axis order to avoid problems with the pop 62 | for ax in sorted(axis)[::-1]: 63 | # Optimization 64 | if(a.shape[ax] > 1): 65 | s = func(a, s, pu.c2f(a.shape, ax)) 66 | if keepdims: 67 | shape[ax] = 1 68 | else: 69 | shape.pop(ax) 70 | ret = afnumpy.ndarray(pu.af_shape(s), dtype=pu.typemap(s.dtype()), 71 | af_array=s).reshape(tuple(shape)) 72 | if(dtype is not None): 73 | ret = ret.astype(dtype) 74 | if(len(shape) == 0): 75 | ret = ret[()] 76 | return ret 77 | return wrapper 78 | -------------------------------------------------------------------------------- /afnumpy/fft.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | from .multiarray import ndarray 3 | import arrayfire 4 | from . import private_utils as pu 5 | import afnumpy 6 | import numbers 7 | 8 | def fft(a, s=None, axes=None): 9 | if(s is None): 10 | s = a.shape[-1:] 11 | return __fftn__(a, s=s, axes=axes, direction='forward') 12 | 13 | def ifft(a, s=None, axes=None): 14 | if(s is None): 15 | s = a.shape[-1:] 16 | return __fftn__(a, s=s, axes=axes, direction='inverse') 17 | 18 | def fft2(a, s=None, axes=None): 19 | if(s is None): 20 | s = a.shape[-2:] 21 | return __fftn__(a, s=s, axes=axes, direction='forward') 22 | 23 | def ifft2(a, s=None, axes=None): 24 | if(s is None): 25 | s = a.shape[-2:] 26 | return __fftn__(a, s=s, axes=axes, direction='inverse') 27 | 28 | def fftn(a, s=None, axes=None): 29 | if(s is None): 30 | s = a.shape 31 | return __fftn__(a, s=s, axes=axes, direction='forward') 32 | 33 | def ifftn(a, s=None, axes=None): 34 | if(s is None): 35 | s = a.shape 36 | return __fftn__(a, s=s, axes=axes, direction='inverse') 37 | 38 | def __fftn__(a, s, axes, direction='forward'): 39 | if len(s) != 3 and len(s) != 2 and len(s) != 1: 40 | raise NotImplementedError 41 | if axes is not None: 42 | raise NotImplementedError 43 | if(direction == 'forward'): 44 | if len(s) == 3: 45 | fa = arrayfire.fft3(a.d_array, s[2], s[1], s[0]) 46 | elif len(s) == 2: 47 | fa = arrayfire.fft2(a.d_array, s[1], s[0]) 48 | elif len(s) == 1: 49 | fa = arrayfire.fft(a.d_array, s[0]) 50 | elif direction == 'inverse': 51 | if len(s) == 3: 52 | fa = arrayfire.ifft3(a.d_array, s[2], s[1], s[0]) 53 | elif len(s) == 2: 54 | fa = arrayfire.ifft2(a.d_array, s[1], s[0]) 55 | elif len(s) == 1: 56 | fa = arrayfire.ifft(a.d_array, s[0]) 57 | else: 58 | raise ValueError('Wrong FFT direction') 59 | return ndarray(pu.af_shape(fa), dtype=pu.typemap(fa.dtype()), af_array=fa) 60 | 61 | 62 | def fftshift(x, axes=None): 63 | tmp = afnumpy.asarray(x) 64 | ndim = len(x.shape) 65 | if axes is None: 66 | axes = list(range(ndim)) 67 | elif isinstance(axes, numbers.Integral): 68 | axes = [axes] 69 | shift = [0]*ndim 70 | for k in axes: 71 | n = x.shape[k] 72 | shift[k] = int(n//2) 73 | s = arrayfire.data.shift(x.d_array, *pu.c2f(shift)) 74 | return ndarray(pu.af_shape(s), dtype=pu.typemap(s.dtype()), af_array=s) 75 | 76 | 77 | def ifftshift(x, axes=None): 78 | tmp = afnumpy.asarray(x) 79 | ndim = len(tmp.shape) 80 | if axes is None: 81 | axes = list(range(ndim)) 82 | elif isinstance(axes, numbers.Integral): 83 | axes = [axes] 84 | shift = [0]*ndim 85 | for k in axes: 86 | n = tmp.shape[k] 87 | shift[k] = (n+1)//2 88 | s = arrayfire.data.shift(tmp.d_array, *pu.c2f(shift)) 89 | return ndarray(pu.af_shape(s), dtype=pu.typemap(s.dtype()), af_array=s) 90 | -------------------------------------------------------------------------------- /afnumpy/__init__.py: -------------------------------------------------------------------------------- 1 | import arrayfire 2 | import numpy 3 | from .multiarray import ndarray 4 | from . import random 5 | from .core import * 6 | from .lib import * 7 | from . import linalg 8 | from .linalg import vdot, dot 9 | import ctypes 10 | 11 | def arrayfire_version(numeric = False): 12 | major = ctypes.c_int(0) 13 | minor = ctypes.c_int(0) 14 | patch = ctypes.c_int(0) 15 | arrayfire.backend.get().af_get_version(ctypes.pointer(major), 16 | ctypes.pointer(minor), 17 | ctypes.pointer(patch)); 18 | if(numeric): 19 | return major.value * 1000000 + minor.value*1000 + patch.value 20 | return '%d.%d.%d' % (major.value, minor.value, patch.value) 21 | 22 | def inplace_setitem(self, key, val): 23 | try: 24 | n_dims = self.numdims() 25 | if (arrayfire.util._is_number(val)): 26 | tdims = arrayfire.array._get_assign_dims(key, self.dims()) 27 | other_arr = arrayfire.array.constant_array(val, tdims[0], tdims[1], tdims[2], tdims[3], self.type()) 28 | del_other = True 29 | else: 30 | other_arr = val.arr 31 | del_other = False 32 | 33 | inds = arrayfire.array._get_indices(key) 34 | 35 | # In place assignment. Notice passing a pointer to self.arr as output 36 | arrayfire.util.safe_call(arrayfire.backend.get().af_assign_gen(ctypes.pointer(self.arr), 37 | self.arr, ctypes.c_longlong(n_dims), 38 | inds.pointer, 39 | other_arr)) 40 | if del_other: 41 | arrayfire.safe_call(arrayfire.backend.get().af_release_array(other_arr)) 42 | except RuntimeError as e: 43 | raise IndexError(str(e)) 44 | 45 | 46 | def raw_ptr(self): 47 | """ 48 | Return the device pointer held by the array. 49 | 50 | Returns 51 | ------ 52 | ptr : int 53 | Contains location of the device pointer 54 | 55 | Note 56 | ---- 57 | - This can be used to integrate with custom C code and / or PyCUDA or PyOpenCL. 58 | - No mem copy is peformed, this function returns the raw device pointer. 59 | """ 60 | ptr = ctypes.c_void_p(0) 61 | arrayfire.backend.get().af_get_raw_ptr(ctypes.pointer(ptr), self.arr) 62 | return ptr.value 63 | 64 | arrayfire.Array.__setitem__ = inplace_setitem 65 | 66 | if arrayfire_version(numeric=True) >= 3003000: 67 | arrayfire.Array.device_ptr = raw_ptr 68 | elif arrayfire_version(numeric=True) >= 3002000: 69 | raise RuntimeError('afnumpy is incompatible with arrayfire 3.2. Please upgrade.') 70 | 71 | 72 | # This defines if we'll try to force JIT evals 73 | # after every instructions. 74 | # We we do not we risk having certain operations out of order 75 | force_eval = True 76 | 77 | # Check arrays for out of bounds indexing 78 | # Also properly handle negative indices 79 | safe_indexing = True 80 | 81 | from .version import __version__, __githash__ 82 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | setup.py file for afnumpy 5 | """ 6 | 7 | from setuptools import setup 8 | import setuptools.command.build_py 9 | import subprocess 10 | import os.path 11 | 12 | version = '1.3' 13 | name = 'afnumpy' 14 | cwd = os.path.dirname(os.path.abspath(__file__)) 15 | 16 | def _get_version_hash(): 17 | """Talk to git and find out the tag/hash of our latest commit""" 18 | try: 19 | p = subprocess.Popen(["git", "rev-parse", "--short" ,"HEAD"], 20 | stdout=subprocess.PIPE) 21 | except EnvironmentError: 22 | print("Couldn't run git to get a version number for setup.py") 23 | return 24 | ver = p.communicate()[0] 25 | return ver.strip().decode("utf-8") 26 | 27 | git_hash = _get_version_hash() 28 | 29 | # Get the long description from the README file 30 | with open(os.path.join(cwd, 'README.md')) as f: 31 | long_description = f.read() 32 | 33 | long_description += "\n### Git commit\n[%s](http://github.com/FilipeMaia/afnumpy/commit/%s)" % (git_hash,git_hash) 34 | 35 | 36 | class Version(setuptools.command.build_py.build_py): 37 | 38 | def run(self): 39 | self.create_version_file() 40 | setuptools.command.build_py.build_py.run(self) 41 | 42 | def create_version_file(self): 43 | print('-- Building version ' + version) 44 | version_path = os.path.join(cwd, name, 'version.py') 45 | with open(version_path, 'w') as f: 46 | f.write("__version__ = '%s'\n" % (version)) 47 | f.write("__githash__ = '%s'\n" % (git_hash)) 48 | 49 | setup (name = name, 50 | version = version, 51 | author = "Filipe Maia", 52 | author_email = "filipe.c.maia@gmail.com", 53 | url = 'https://github.com/FilipeMaia/afnumpy', 54 | download_url = 'https://github.com/FilipeMaia/afnumpy/archive/'+version+'.tar.gz', 55 | keywords = ['arrayfire', 'numpy', 'GPU'], 56 | description = """A GPU-ready drop-in replacement for numpy""", 57 | long_description= long_description, 58 | long_description_content_type='text/markdown', 59 | packages = ["afnumpy", "afnumpy/core", "afnumpy/lib", "afnumpy/linalg", "afnumpy/sparse"], 60 | install_requires=['arrayfire', 'numpy>=1.9.0'], 61 | classifiers=[ 62 | # How mature is this project? Common values are 63 | # 3 - Alpha 64 | # 4 - Beta 65 | # 5 - Production/Stable 66 | 'Development Status :: 3 - Alpha', 67 | 68 | # Indicate who your project is intended for 69 | 'Intended Audience :: Science/Research', 70 | 'Topic :: Scientific/Engineering :: Physics', 71 | 72 | # Pick your license as you wish (should match "license" above) 73 | 'License :: OSI Approved :: BSD License', 74 | 75 | # Specify the Python versions you support here. In particular, ensure 76 | # that you indicate whether you support Python 2, Python 3 or both. 77 | 'Programming Language :: Python :: 2', 78 | 'Programming Language :: Python :: 2.6', 79 | 'Programming Language :: Python :: 2.7', 80 | #'Programming Language :: Python :: 3', 81 | #'Programming Language :: Python :: 3.2', 82 | #'Programming Language :: Python :: 3.3', 83 | #'Programming Language :: Python :: 3.4', 84 | ], 85 | license='BSD', 86 | cmdclass={"build_py": Version}, 87 | tests_require=['pytest','pytest-cov','pytest-benchmark','arrayfire','numpy>=1.9.0'], 88 | setup_requires=["pytest-runner"], 89 | ) 90 | -------------------------------------------------------------------------------- /afnumpy/lib/function_base.py: -------------------------------------------------------------------------------- 1 | import afnumpy 2 | import numpy 3 | import collections 4 | 5 | def copy(a, order='K'): 6 | return afnumpy.array(a, order=order, copy=True) 7 | 8 | def meshgrid(*xi, **kwargs): 9 | ndim = len(xi) 10 | 11 | copy_ = kwargs.pop('copy', True) 12 | sparse = kwargs.pop('sparse', False) 13 | indexing = kwargs.pop('indexing', 'xy') 14 | 15 | if kwargs: 16 | raise TypeError("meshgrid() got an unexpected keyword argument '%s'" 17 | % (list(kwargs)[0],)) 18 | 19 | if indexing not in ['xy', 'ij']: 20 | raise ValueError( 21 | "Valid values for `indexing` are 'xy' and 'ij'.") 22 | 23 | s0 = (1,) * ndim 24 | 25 | output = [afnumpy.asanyarray(x).reshape(s0[:i] + (-1,) + s0[i + 1::]) 26 | for i, x in enumerate(xi)] 27 | 28 | shape = [int(x.size) for x in output] 29 | 30 | if indexing == 'xy' and ndim > 1: 31 | # switch first and second axis 32 | output[0].shape = (1, -1) + (1,)*(ndim - 2) 33 | output[1].shape = (-1, 1) + (1,)*(ndim - 2) 34 | shape[0], shape[1] = shape[1], shape[0] 35 | 36 | if sparse: 37 | if copy_: 38 | return [x.copy() for x in output] 39 | else: 40 | return output 41 | else: 42 | # Return the full N-D matrix (not only the 1-D vector) 43 | if copy_: 44 | # Numpy uses dtype=int but Arrayfire does not support int64 in all functions 45 | mult_fact = afnumpy.ones(shape, dtype=numpy.int32) 46 | return [x * mult_fact for x in output] 47 | else: 48 | return afnumpy.broadcast_arrays(*output) 49 | 50 | def angle(z, deg=0): 51 | """ 52 | Return the angle of the complex argument. 53 | 54 | Parameters 55 | ---------- 56 | z : array_like 57 | A complex number or sequence of complex numbers. 58 | deg : bool, optional 59 | Return angle in degrees if True, radians if False (default). 60 | 61 | Returns 62 | ------- 63 | angle : {ndarray, scalar} 64 | The counterclockwise angle from the positive real axis on 65 | the complex plane, with dtype as numpy.float64. 66 | 67 | See Also 68 | -------- 69 | arctan2 70 | absolute 71 | 72 | 73 | 74 | Examples 75 | -------- 76 | >>> np.angle([1.0, 1.0j, 1+1j]) # in radians 77 | array([ 0. , 1.57079633, 0.78539816]) 78 | >>> np.angle(1+1j, deg=True) # in degrees 79 | 45.0 80 | 81 | """ 82 | if deg: 83 | fact = 180/pi 84 | else: 85 | fact = 1.0 86 | z = afnumpy.asarray(z) 87 | if numpy.issubdtype(z.dtype, numpy.complexfloating): 88 | zimag = z.imag 89 | zreal = z.real 90 | else: 91 | zimag = 0 92 | zreal = z 93 | return afnumpy.arctan2(zimag, zreal) * fact 94 | 95 | def percentile(a, q, axis=None, out=None, 96 | overwrite_input=False, interpolation='linear', keepdims=False): 97 | if interpolation != 'linear': 98 | raise ValueError('Only linear interpolation is supported') 99 | if out is not None: 100 | raise ValueError('"out" parameter is not supported') 101 | if isinstance(axis, collections.Sequence): 102 | raise ValueError('axis sequences not supported') 103 | 104 | if axis is None: 105 | input = a.flatten() 106 | axis = 0 107 | else: 108 | input = a 109 | s = afnumpy.sort(input,axis=axis) 110 | low_idx = numpy.floor(q*(s.shape[axis]-1)/100.0) 111 | high_idx = numpy.ceil(q*(s.shape[axis]-1)/100.0) 112 | u = (q*(s.shape[axis]-1)/100.0) - low_idx 113 | low = afnumpy.take(s, low_idx, axis=axis) 114 | high = afnumpy.take(s, high_idx, axis=axis) 115 | ret = u*high+(1-u)*low 116 | if keepdims is True: 117 | ret.reshape(ret.shape[:axis]+(1,)+ret.shape[axis:]) 118 | return ret 119 | 120 | 121 | -------------------------------------------------------------------------------- /examples/phase_retrieval.py: -------------------------------------------------------------------------------- 1 | #!/usr/env python 2 | import sys 3 | import time 4 | import argparse 5 | from scipy.ndimage import gaussian_filter 6 | from scipy.ndimage.interpolation import rotate 7 | 8 | parser = argparse.ArgumentParser() 9 | group = parser.add_mutually_exclusive_group(required=True) 10 | group.add_argument('-a', '--use-afnumpy',action='store_true') 11 | group.add_argument('-n', '--use-numpy', action='store_true') 12 | parser.add_argument('-p', '--plotting', action='store_true') 13 | parser.add_argument('-d', '--debug', action='store_true') 14 | args = parser.parse_args() 15 | 16 | # Generate test dataset 17 | import numpy as np 18 | shape = (512,512) 19 | center = (int((shape[0])/2), int((shape[1])/2)) 20 | radius = 50 21 | solution = np.zeros(shape) 22 | solution[center[0]-radius:center[0]+radius-1,center[1]-radius:center[1]+radius-1] = 1. 23 | solution += rotate(solution, -45, reshape=False) 24 | solution = gaussian_filter(solution, radius//10) 25 | 26 | # Switch between numpy/afnumpy 27 | if args.use_afnumpy: 28 | import afnumpy as np 29 | import afnumpy.fft as fft 30 | use = 'afnumpy/GPU' 31 | elif args.use_numpy: 32 | import numpy as np 33 | import numpy.fft as fft 34 | use = 'numpy/CPU' 35 | 36 | radius = np.array(radius) 37 | shape = (np.array(shape[0]), np.array(shape[1])) 38 | solution = np.array(solution) 39 | fourier = np.fft.fftshift(np.fft.fft2(solution)) 40 | intensities = np.abs(fourier)**2 41 | 42 | # Initial (random) image 43 | image = (1j + np.random.random(intensities.shape)).astype(np.complex64) 44 | 45 | # Define support 46 | yy,xx = np.meshgrid(np.arange(image.shape[0]), np.arange(image.shape[1])) 47 | rr = np.sqrt((xx-image.shape[1]/2)**2 + (yy-image.shape[0]/2)**2) 48 | support = rr < np.sqrt(2) * radius - 1 49 | 50 | # Define Nr. of iterations 51 | nr_iterations = 500 52 | 53 | # Define error that defines convergence 54 | def get_error(fourier, intensities): 55 | return np.abs((np.sqrt(intensities) - np.abs(fourier)).sum()) / np.sqrt(intensities).sum() 56 | 57 | # Time the reconstruction 58 | t0 = time.time() 59 | 60 | # Store error 61 | error = [] 62 | 63 | # Start the reconstruction (using ER) 64 | for i in range(nr_iterations): 65 | 66 | # Forward propagation 67 | fourier = fft.fftn(image) 68 | 69 | # Check convergence 70 | error.append(get_error(fourier, intensities)) 71 | if args.debug: 72 | print("Iteration: %d, error: %f" %(i, error[-1])) 73 | 74 | # Apply data constraint 75 | fourier /= np.abs(fourier) 76 | fourier *= np.sqrt(intensities) 77 | 78 | # Backward propagation 79 | image = fft.ifftn(fourier) 80 | 81 | # Apply support constraint 82 | image *= support 83 | 84 | # Timing 85 | t1 = time.time() - t0 86 | success = np.sum(((np.abs(image) - solution)**2)*support) / (shape[0] * shape[1]) < 1e-2 87 | print("Success: %d, %d Iterations took %2.f seconds (%.2f iterations per second) using %s" %(success, nr_iterations, t1, float(nr_iterations)/t1, use)) 88 | 89 | #Check for plotting 90 | if not args.plotting: 91 | sys.exit(0) 92 | 93 | # Plotting the result 94 | try: 95 | import matplotlib 96 | #matplotlib.use('Agg') 97 | import matplotlib.pyplot as plt 98 | import matplotlib.colors as colors 99 | except ImportError: 100 | print("Could not find matplotlib, no plots produced") 101 | sys.exit(0) 102 | 103 | fig = plt.figure(figsize=(7,10)) 104 | ax5 = plt.subplot(313) 105 | ax1 = plt.subplot(321) 106 | ax2 = plt.subplot(322) 107 | ax3 = plt.subplot(323) 108 | ax4 = plt.subplot(324) 109 | plt.subplots_adjust(wspace=0.05, hspace=0.05) 110 | axes = [ax1,ax2,ax3,ax4] 111 | for i in range(4): 112 | axes[i].set_yticklabels([]) 113 | axes[i].set_xticklabels([]) 114 | axes[i].set_yticks([]) 115 | axes[i].set_xticks([]) 116 | 117 | plt.tight_layout() 118 | ax1.imshow(np.abs(image), vmin=solution.min(), vmax=solution.max()) 119 | ax2.imshow(np.abs(image)-solution, vmin=-1, vmax=1) 120 | ax3.imshow(np.angle(fourier), vmin=-np.pi, vmax=np.pi) 121 | ax4.imshow(intensities, norm=colors.LogNorm()) 122 | l, = ax5.plot(error) 123 | ax5.semilogy() 124 | ax5.text(0.5, 0.9, 'Iteration = %d' %nr_iterations, transform=ax5.transAxes, va='center', ha='center') 125 | plt.show() 126 | -------------------------------------------------------------------------------- /afnumpy/private_utils.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | import numbers 3 | import afnumpy 4 | import arrayfire 5 | 6 | dim_t = numpy.dtype('int64') 7 | 8 | try: 9 | TypeToString = { arrayfire.f64.value: 'f64', 10 | arrayfire.f32.value: 'f32', 11 | arrayfire.u32.value: 'u32', 12 | arrayfire.s32.value: 's32', 13 | arrayfire.u64.value: 'u64', 14 | arrayfire.s64.value: 's64', 15 | arrayfire.c32.value: 'c32', 16 | arrayfire.c64.value: 'c64', 17 | arrayfire.b8.value: 'b8', 18 | } 19 | except AttributeError: 20 | TypeToString = { arrayfire.Dtype.f64: 'f64', 21 | arrayfire.Dtype.f32: 'f32', 22 | arrayfire.Dtype.u32: 'u32', 23 | arrayfire.Dtype.s32: 's32', 24 | arrayfire.Dtype.u64: 'u64', 25 | arrayfire.Dtype.s64: 's64', 26 | arrayfire.Dtype.c32: 'c32', 27 | arrayfire.Dtype.c64: 'c64', 28 | arrayfire.Dtype.b8: 'b8', 29 | } 30 | 31 | 32 | dummy = object() 33 | 34 | def af_shape(af_array): 35 | return tuple(af_array.dims()[::-1]) 36 | 37 | def raw(x): 38 | if(isinstance(x,afnumpy.ndarray)): 39 | return x.d_array 40 | # elif(isinstance(x,complex)): 41 | # return arrayfire.af_cdouble(x.real, x.imag) 42 | else: 43 | return x 44 | 45 | def c2f(shape, dim = None): 46 | if isinstance(shape,numbers.Number): 47 | return shape 48 | if(dim is None): 49 | return shape[::-1] 50 | else: 51 | return len(shape)-dim-1 52 | 53 | 54 | def typemap(dtype): 55 | try: 56 | InvTypeMap = {arrayfire.f64.value: numpy.dtype('float64'), 57 | arrayfire.f32.value: numpy.dtype('float32'), 58 | arrayfire.c64.value: numpy.dtype('complex128'), 59 | arrayfire.c32.value: numpy.dtype('complex64'), 60 | arrayfire.s32.value: numpy.dtype('int32'), 61 | arrayfire.s64.value: numpy.dtype('int64'), 62 | arrayfire.u32.value: numpy.dtype('uint32'), 63 | arrayfire.u64.value: numpy.dtype('uint64'), 64 | arrayfire.b8.value: numpy.dtype('bool'), 65 | } 66 | TypeMap = {numpy.dtype('float32'): arrayfire.f32.value, 67 | numpy.dtype('float64'): arrayfire.f64.value, 68 | numpy.dtype('int8'): arrayfire.b8.value, 69 | numpy.dtype('uint8'): arrayfire.u8.value, 70 | numpy.dtype('bool'): arrayfire.b8.value, 71 | numpy.dtype('int64'): arrayfire.s64.value, 72 | numpy.dtype('uint64'): arrayfire.u64.value, 73 | numpy.dtype('uint32'): arrayfire.u32.value, 74 | numpy.dtype('int32'): arrayfire.s32.value, 75 | numpy.dtype('complex128'): arrayfire.c64.value, 76 | numpy.dtype('complex64'): arrayfire.c32.value, 77 | } 78 | except AttributeError: 79 | InvTypeMap = {arrayfire.Dtype.f64: numpy.dtype('float64'), 80 | arrayfire.Dtype.f32: numpy.dtype('float32'), 81 | arrayfire.Dtype.c64: numpy.dtype('complex128'), 82 | arrayfire.Dtype.c32: numpy.dtype('complex64'), 83 | arrayfire.Dtype.s32: numpy.dtype('int32'), 84 | arrayfire.Dtype.s64: numpy.dtype('int64'), 85 | arrayfire.Dtype.u32: numpy.dtype('uint32'), 86 | arrayfire.Dtype.u64: numpy.dtype('uint64'), 87 | arrayfire.Dtype.b8: numpy.dtype('bool'), 88 | } 89 | TypeMap = {numpy.dtype('float32'): arrayfire.Dtype.f32, 90 | numpy.dtype('float64'): arrayfire.Dtype.f64, 91 | numpy.dtype('int8'): arrayfire.Dtype.b8, 92 | numpy.dtype('uint8'): arrayfire.Dtype.u8, 93 | numpy.dtype('bool'): arrayfire.Dtype.b8, 94 | numpy.dtype('int64'): arrayfire.Dtype.s64, 95 | numpy.dtype('uint64'): arrayfire.Dtype.u64, 96 | numpy.dtype('uint32'): arrayfire.Dtype.u32, 97 | numpy.dtype('int32'): arrayfire.Dtype.s32, 98 | numpy.dtype('complex128'): arrayfire.Dtype.c64, 99 | numpy.dtype('complex64'): arrayfire.Dtype.c32, 100 | } 101 | 102 | if(dtype in InvTypeMap): 103 | return InvTypeMap[dtype] 104 | else: 105 | dtype = numpy.dtype(dtype) 106 | return TypeMap[dtype] 107 | 108 | 109 | 110 | def isintegertype(obj): 111 | if isinstance(obj, numbers.Number): 112 | return numpy.issubdtype(type(obj), numpy.integer) 113 | return numpy.issubdtype(obj.dtype, numpy.integer) 114 | -------------------------------------------------------------------------------- /afnumpy/lib/scimath.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | import afnumpy 3 | import arrayfire 4 | from .. import private_utils as pu 5 | from ..decorators import * 6 | 7 | @outufunc 8 | def arccos(x): 9 | if isinstance(x, afnumpy.ndarray): 10 | s = arrayfire.acos(x.d_array) 11 | return afnumpy.ndarray(x.shape, dtype=pu.typemap(s.dtype()), af_array=s) 12 | else: 13 | return numpy.arccos(x) 14 | 15 | @outufunc 16 | def arcsin(x): 17 | if isinstance(x, afnumpy.ndarray): 18 | s = arrayfire.asin(x.d_array) 19 | return afnumpy.ndarray(x.shape, dtype=pu.typemap(s.dtype()), af_array=s) 20 | else: 21 | return numpy.arcsin(x) 22 | 23 | @outufunc 24 | def arctan(x): 25 | if isinstance(x, afnumpy.ndarray): 26 | s = arrayfire.atan(x.d_array) 27 | return afnumpy.ndarray(x.shape, dtype=pu.typemap(s.dtype()), af_array=s) 28 | else: 29 | return numpy.arctan(x) 30 | 31 | @outufunc 32 | def arctan2(x1, x2): 33 | if isinstance(x1, afnumpy.ndarray) and isinstance(x2, afnumpy.ndarray): 34 | s = arrayfire.atan2(x1.d_array, x2.d_array) 35 | return afnumpy.ndarray(x1.shape, dtype=pu.typemap(s.dtype()), af_array=s) 36 | else: 37 | return numpy.arctan(x1, x2) 38 | 39 | @outufunc 40 | def arccosh(x): 41 | if isinstance(x, afnumpy.ndarray): 42 | s = arrayfire.acosh(x.d_array) 43 | return afnumpy.ndarray(x.shape, dtype=pu.typemap(s.dtype()), af_array=s) 44 | else: 45 | return numpy.arccosh(x) 46 | 47 | @outufunc 48 | def arcsinh(x): 49 | if isinstance(x, afnumpy.ndarray): 50 | s = arrayfire.asinh(x.d_array) 51 | return afnumpy.ndarray(x.shape, dtype=pu.typemap(s.dtype()), af_array=s) 52 | else: 53 | return numpy.arcsinh(x) 54 | 55 | @outufunc 56 | def arctanh(x): 57 | if isinstance(x, afnumpy.ndarray): 58 | s = arrayfire.atanh(x.d_array) 59 | return afnumpy.ndarray(x.shape, dtype=pu.typemap(s.dtype()), af_array=s) 60 | else: 61 | return numpy.arctanh(x) 62 | 63 | 64 | @outufunc 65 | def cos(x): 66 | if isinstance(x, afnumpy.ndarray): 67 | s = arrayfire.cos(x.d_array) 68 | return afnumpy.ndarray(x.shape, dtype=pu.typemap(s.dtype()), af_array=s) 69 | else: 70 | return numpy.cos(x) 71 | 72 | @outufunc 73 | def sin(x): 74 | if isinstance(x, afnumpy.ndarray): 75 | s = arrayfire.sin(x.d_array) 76 | return afnumpy.ndarray(x.shape, dtype=pu.typemap(s.dtype()), af_array=s) 77 | else: 78 | return numpy.sin(x) 79 | 80 | @outufunc 81 | def tan(x): 82 | if isinstance(x, afnumpy.ndarray): 83 | s = arrayfire.tan(x.d_array) 84 | return afnumpy.ndarray(x.shape, dtype=pu.typemap(s.dtype()), af_array=s) 85 | else: 86 | return numpy.tan(x) 87 | 88 | @outufunc 89 | def cosh(x): 90 | if isinstance(x, afnumpy.ndarray): 91 | s = arrayfire.cosh(x.d_array) 92 | return afnumpy.ndarray(x.shape, dtype=pu.typemap(s.dtype()), af_array=s) 93 | else: 94 | return numpy.cosh(x) 95 | 96 | @outufunc 97 | def sinh(x): 98 | if isinstance(x, afnumpy.ndarray): 99 | s = arrayfire.sinh(x.d_array) 100 | return afnumpy.ndarray(x.shape, dtype=pu.typemap(s.dtype()), af_array=s) 101 | else: 102 | return numpy.sinh(x) 103 | 104 | @outufunc 105 | def tanh(x): 106 | if isinstance(x, afnumpy.ndarray): 107 | s = arrayfire.tanh(x.d_array) 108 | return afnumpy.ndarray(x.shape, dtype=pu.typemap(s.dtype()), af_array=s) 109 | else: 110 | return numpy.tanh(x) 111 | 112 | @outufunc 113 | def exp(x): 114 | if isinstance(x, afnumpy.ndarray): 115 | s = arrayfire.exp(x.d_array) 116 | return afnumpy.ndarray(x.shape, dtype=pu.typemap(s.dtype()), af_array=s) 117 | else: 118 | return numpy.exp(x) 119 | 120 | @outufunc 121 | def log(x): 122 | if isinstance(x, afnumpy.ndarray): 123 | s = arrayfire.log(x.d_array) 124 | return afnumpy.ndarray(x.shape, dtype=pu.typemap(s.dtype()), af_array=s) 125 | else: 126 | return numpy.log(x) 127 | 128 | @outufunc 129 | def log10(x): 130 | if isinstance(x, afnumpy.ndarray): 131 | s = arrayfire.log10(x.d_array) 132 | return afnumpy.ndarray(x.shape, dtype=pu.typemap(s.dtype()), af_array=s) 133 | else: 134 | return numpy.log10(x) 135 | 136 | def real(x): 137 | return afnumpy.asanyarray(x).real 138 | 139 | def imag(x): 140 | return afnumpy.asanyarray(x).imag 141 | 142 | @outufunc 143 | def multiply(x1, x2): 144 | return x1*x2 145 | 146 | @outufunc 147 | def subtract(x1, x2): 148 | return x1-x2 149 | 150 | @outufunc 151 | def add(x1, x2): 152 | return x1+x2 153 | 154 | @outufunc 155 | def divide(x1, x2): 156 | return x1/x2 157 | 158 | @outufunc 159 | def floor_divide(x1, x2): 160 | return x1//x2 161 | 162 | @outufunc 163 | def true_divide(x1, x2): 164 | return x1/x2 165 | 166 | @outufunc 167 | def conjugate(x): 168 | if isinstance(x, afnumpy.ndarray): 169 | return x.conj() 170 | else: 171 | return numpy.conjugate(x) 172 | 173 | conj = conjugate 174 | 175 | inf = numpy.inf 176 | Inf = numpy.Inf 177 | Infinity = numpy.Infinity 178 | pi = numpy.pi 179 | e = numpy.e 180 | 181 | -------------------------------------------------------------------------------- /afnumpy/core/multiarray.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | import afnumpy 3 | import arrayfire 4 | from .. import private_utils as pu 5 | from ..decorators import * 6 | 7 | 8 | def fromstring(string, dtype=float, count=-1, sep=''): 9 | return array(numpy.fromstring(string, dtype, count, sep)) 10 | 11 | def array(object, dtype=None, copy=True, order=None, subok=False, ndmin=0): 12 | # We're going to ignore this for now 13 | # if(subok is not False): 14 | # raise NotImplementedError 15 | if(order is not None and order != 'K' and order != 'C'): 16 | raise NotImplementedError 17 | 18 | # If it's not a numpy or afnumpy array first create a numpy array from it 19 | if(not isinstance(object, afnumpy.ndarray) and 20 | not isinstance(object, numpy.ndarray) and 21 | not isinstance(object, arrayfire.array.Array)): 22 | object = numpy.array(object, dtype=dtype, copy=copy, order=order, subok=subok, ndmin=ndmin) 23 | 24 | if isinstance(object, arrayfire.array.Array): 25 | shape = pu.c2f(object.dims()) 26 | else: 27 | shape = object.shape 28 | while(ndmin > len(shape)): 29 | shape = (1,)+shape 30 | if(dtype is None): 31 | if isinstance(object, arrayfire.array.Array): 32 | dtype = pu.typemap(object.dtype()) 33 | else: 34 | dtype = object.dtype 35 | if(isinstance(object, afnumpy.ndarray)): 36 | if(copy): 37 | s = arrayfire.cast(object.d_array.copy(), pu.typemap(dtype)) 38 | else: 39 | s = arrayfire.cast(object.d_array, pu.typemap(dtype)) 40 | a = afnumpy.ndarray(shape, dtype=dtype, af_array=s) 41 | a._eval() 42 | return a 43 | elif(isinstance(object, arrayfire.array.Array)): 44 | if(copy): 45 | s = arrayfire.cast(object.copy(), pu.typemap(dtype)) 46 | else: 47 | s = arrayfire.cast(object, pu.typemap(dtype)) 48 | a = afnumpy.ndarray(shape, dtype=dtype, af_array=s) 49 | a._eval() 50 | return a 51 | elif(isinstance(object, numpy.ndarray)): 52 | return afnumpy.ndarray(shape, dtype=dtype, buffer=numpy.ascontiguousarray(object.astype(dtype, copy=copy))) 53 | else: 54 | raise AssertionError 55 | 56 | def arange(start, stop = None, step = None, dtype=None): 57 | return afnumpy.array(numpy.arange(start,stop,step,dtype)) 58 | 59 | def empty(shape, dtype=float, order='C'): 60 | return afnumpy.ndarray(shape, dtype=dtype, order=order) 61 | 62 | def zeros(shape, dtype=float, order='C'): 63 | b = numpy.zeros(shape, dtype, order) 64 | return afnumpy.ndarray(b.shape, b.dtype, buffer=b,order=order) 65 | 66 | def where(condition, x=pu.dummy, y=pu.dummy): 67 | a = condition 68 | s = arrayfire.where(a.d_array) 69 | # numpy uses int64 while arrayfire uses uint32 70 | s = afnumpy.ndarray(pu.af_shape(s), dtype=numpy.uint32, af_array=s).astype(numpy.int64) 71 | # Looks like where goes through the JIT?? 72 | s.eval() 73 | if(x is pu.dummy and y is pu.dummy): 74 | idx = [] 75 | mult = 1 76 | for i in a.shape[::-1]: 77 | mult = i 78 | idx = [s % mult] + idx 79 | s //= mult 80 | idx = tuple(idx) 81 | return idx 82 | elif(x is not pu.dummy and y is not pu.dummy): 83 | if(x.dtype != y.dtype): 84 | raise TypeError('x and y must have same dtype') 85 | if(x.shape != y.shape): 86 | raise ValueError('x and y must have same shape') 87 | ret = afnumpy.array(y) 88 | if(len(ret.shape) > 1): 89 | ret = ret.flatten() 90 | ret[s] = x.flatten()[s] 91 | ret = ret.reshape(x.shape) 92 | else: 93 | ret[s] = x[s] 94 | return ret; 95 | else: 96 | raise ValueError('either both or neither of x and y should be given') 97 | 98 | def concatenate(arrays, axis=0): 99 | arrays = tuple(arrays) 100 | if len(arrays) == 0: 101 | raise ValueError('need at least one array to concatenate') 102 | base = arrays[0] 103 | if len(arrays) == 1: 104 | return base.copy() 105 | # arrayfire accepts at most 4 arrays to concatenate at once so we'll have 106 | # to chunk the arrays 107 | # The first case is special as we don't want to create unnecessary copies 108 | i = 0 109 | a = arrays[i].d_array 110 | if i+1 < len(arrays): 111 | b = arrays[i+1].d_array 112 | else: 113 | b = None 114 | if i+2 < len(arrays): 115 | c = arrays[i+2].d_array 116 | else: 117 | c = None 118 | if i+3 < len(arrays): 119 | d = arrays[i+3].d_array 120 | else: 121 | d = None 122 | ret = arrayfire.join(pu.c2f(arrays[0].shape, axis), a, b, c, d) 123 | 124 | for i in range(4,len(arrays),4): 125 | a = ret.d_array 126 | if i < len(arrays): 127 | b = arrays[i].d_array 128 | else: 129 | b = None 130 | if i+1 < len(arrays): 131 | c = arrays[i+1].d_array 132 | else: 133 | c = None 134 | if i+2 < len(arrays): 135 | d = arrays[i+2].d_array 136 | else: 137 | d = None 138 | 139 | ret = arrayfire.join(pu.c2f(arrays[0].shape, axis), a, b, c, d) 140 | 141 | return ret 142 | -------------------------------------------------------------------------------- /tests/test_fft.py: -------------------------------------------------------------------------------- 1 | import afnumpy 2 | import afnumpy.fft 3 | import numpy 4 | import numpy.fft 5 | from asserts import * 6 | 7 | def test_fft(): 8 | b = numpy.random.random((3,3)) 9 | a = afnumpy.array(b) 10 | fassert(afnumpy.fft.fft(a), numpy.fft.fft(b)) 11 | 12 | b = numpy.random.random((3,2)) 13 | a = afnumpy.array(b) 14 | fassert(afnumpy.fft.fft(a), numpy.fft.fft(b)) 15 | 16 | b = numpy.random.random((5,3,2)) 17 | a = afnumpy.array(b) 18 | fassert(afnumpy.fft.fft(a), numpy.fft.fft(b)) 19 | 20 | b = numpy.random.random((5,3,2))+numpy.random.random((5,3,2))*1.0j 21 | a = afnumpy.array(b) 22 | fassert(afnumpy.fft.fft(a), numpy.fft.fft(b)) 23 | 24 | def test_ifft(): 25 | # Real to complex inverse fft not implemented in arrayfire 26 | # b = numpy.random.random((3,3)) 27 | # a = afnumpy.array(b) 28 | # fassert(afnumpy.fft.ifft(a), numpy.fft.ifft(b)) 29 | 30 | # b = numpy.random.random((3,2)) 31 | # a = afnumpy.array(b) 32 | # fassert(afnumpy.fft.ifft(a), numpy.fft.ifft(b)) 33 | 34 | # b = numpy.random.random((5,3,2)) 35 | # a = afnumpy.array(b) 36 | # fassert(afnumpy.fft.ifft(a), numpy.fft.ifft(b)) 37 | 38 | b = numpy.random.random((5,3,2))+numpy.random.random((5,3,2))*1.0j 39 | # b = numpy.ones((3,3))+numpy.zeros((3,3))*1.0j 40 | a = afnumpy.array(b) 41 | fassert(afnumpy.fft.ifft(a), numpy.fft.ifft(b)) 42 | 43 | def test_fft2(): 44 | b = numpy.random.random((3,3)) 45 | a = afnumpy.array(b) 46 | fassert(afnumpy.fft.fft2(a), numpy.fft.fft2(b)) 47 | 48 | b = numpy.random.random((3,2)) 49 | a = afnumpy.array(b) 50 | fassert(afnumpy.fft.fft2(a), numpy.fft.fft2(b)) 51 | 52 | b = numpy.random.random((5,3,2)) 53 | a = afnumpy.array(b) 54 | fassert(afnumpy.fft.fft2(a), numpy.fft.fft2(b)) 55 | 56 | b = numpy.random.random((5,3,2))+numpy.random.random((5,3,2))*1.0j 57 | a = afnumpy.array(b) 58 | fassert(afnumpy.fft.fft2(a), numpy.fft.fft2(b)) 59 | 60 | def test_ifft2(): 61 | # Real to complex inverse fft not implemented in arrayfire 62 | # b = numpy.random.random((3,3)) 63 | # a = afnumpy.array(b) 64 | # fassert(afnumpy.fft.ifft2(a), numpy.fft.ifft2(b)) 65 | 66 | # b = numpy.random.random((3,2)) 67 | # a = afnumpy.array(b) 68 | # fassert(afnumpy.fft.ifft2(a), numpy.fft.ifft2(b)) 69 | 70 | # b = numpy.random.random((5,3,2)) 71 | # a = afnumpy.array(b) 72 | # fassert(afnumpy.fft.ifft2(a), numpy.fft.ifft2(b)) 73 | 74 | b = numpy.random.random((5,3,2))+numpy.random.random((5,3,2))*1.0j 75 | # b = numpy.ones((3,3))+numpy.zeros((3,3))*1.0j 76 | a = afnumpy.array(b) 77 | fassert(afnumpy.fft.ifft2(a), numpy.fft.ifft2(b)) 78 | 79 | def test_fftn(): 80 | b = numpy.random.random((3,3)) 81 | a = afnumpy.array(b) 82 | fassert(afnumpy.fft.fftn(a), numpy.fft.fftn(b)) 83 | 84 | b = numpy.random.random((3,2)) 85 | a = afnumpy.array(b) 86 | fassert(afnumpy.fft.fftn(a), numpy.fft.fftn(b)) 87 | 88 | b = numpy.random.random((5,3,2)) 89 | a = afnumpy.array(b) 90 | fassert(afnumpy.fft.fftn(a), numpy.fft.fftn(b)) 91 | 92 | b = numpy.random.random((5,3,2))+numpy.random.random((5,3,2))*1.0j 93 | a = afnumpy.array(b) 94 | fassert(afnumpy.fft.fftn(a), numpy.fft.fftn(b)) 95 | 96 | # Test shape argument 97 | b = numpy.random.random((3,3)) 98 | a = afnumpy.array(b) 99 | s = (3,3) 100 | fassert(afnumpy.fft.fftn(a, s), numpy.fft.fftn(b, s)) 101 | s = (3,6) 102 | fassert(afnumpy.fft.fftn(a, s), numpy.fft.fftn(b, s)) 103 | s = (3,2) 104 | fassert(afnumpy.fft.fftn(a, s), numpy.fft.fftn(b, s)) 105 | 106 | def test_ifftn(): 107 | # Real to complex inverse fft not implemented in arrayfire 108 | # b = numpy.random.random((3,3)) 109 | # a = afnumpy.array(b) 110 | # fassert(afnumpy.fft.ifftn(a), numpy.fft.ifftn(b)) 111 | 112 | # b = numpy.random.random((3,2)) 113 | # a = afnumpy.array(b) 114 | # fassert(afnumpy.fft.ifftn(a), numpy.fft.ifftn(b)) 115 | 116 | # b = numpy.random.random((5,3,2)) 117 | # a = afnumpy.array(b) 118 | # fassert(afnumpy.fft.ifftn(a), numpy.fft.ifftn(b)) 119 | 120 | b = numpy.random.random((5,3,2))+numpy.random.random((5,3,2))*1.0j 121 | # b = numpy.ones((3,3))+numpy.zeros((3,3))*1.0j 122 | a = afnumpy.array(b) 123 | fassert(afnumpy.fft.ifftn(a), numpy.fft.ifftn(b)) 124 | 125 | # Test shape argument 126 | b = numpy.random.random((3,3)) 127 | a = afnumpy.array(b) 128 | s = (3,3) 129 | fassert(afnumpy.fft.ifftn(a, s), numpy.fft.ifftn(b, s)) 130 | s = (3,6) 131 | fassert(afnumpy.fft.ifftn(a, s), numpy.fft.ifftn(b, s)) 132 | s = (3,2) 133 | fassert(afnumpy.fft.ifftn(a, s), numpy.fft.ifftn(b, s)) 134 | 135 | def test_fftshift(): 136 | b = numpy.random.random((3)) 137 | a = afnumpy.array(b) 138 | fassert(afnumpy.fft.fftshift(a), numpy.fft.fftshift(b)) 139 | b = numpy.random.random((3,3)) 140 | a = afnumpy.array(b) 141 | fassert(afnumpy.fft.fftshift(a), numpy.fft.fftshift(b)) 142 | b = numpy.random.random((3,3,3)) 143 | a = afnumpy.array(b) 144 | fassert(afnumpy.fft.fftshift(a), numpy.fft.fftshift(b)) 145 | fassert(afnumpy.fft.fftshift(a,axes=0), numpy.fft.fftshift(b,axes=0)) 146 | fassert(afnumpy.fft.fftshift(a,axes=1), numpy.fft.fftshift(b,axes=1)) 147 | fassert(afnumpy.fft.fftshift(a,axes=2), numpy.fft.fftshift(b,axes=2)) 148 | fassert(afnumpy.fft.fftshift(a,axes=(1,2)), numpy.fft.fftshift(b,axes=(1,2))) 149 | 150 | def test_ifftshift(): 151 | b = numpy.random.random((3)) 152 | a = afnumpy.array(b) 153 | fassert(afnumpy.fft.ifftshift(a), numpy.fft.ifftshift(b)) 154 | b = numpy.random.random((3,3)) 155 | a = afnumpy.array(b) 156 | fassert(afnumpy.fft.ifftshift(a), numpy.fft.ifftshift(b)) 157 | b = numpy.random.random((3,3,3)) 158 | a = afnumpy.array(b) 159 | fassert(afnumpy.fft.ifftshift(a), numpy.fft.ifftshift(b)) 160 | fassert(afnumpy.fft.ifftshift(a,axes=0), numpy.fft.ifftshift(b,axes=0)) 161 | fassert(afnumpy.fft.ifftshift(a,axes=1), numpy.fft.ifftshift(b,axes=1)) 162 | fassert(afnumpy.fft.ifftshift(a,axes=2), numpy.fft.ifftshift(b,axes=2)) 163 | fassert(afnumpy.fft.ifftshift(a,axes=(1,2)), numpy.fft.ifftshift(b,axes=(1,2))) 164 | -------------------------------------------------------------------------------- /afnumpy/core/dtypes.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | import afnumpy 3 | import numbers 4 | 5 | 6 | class int64(numpy.int64): 7 | def __new__(cls, x=0): 8 | if isinstance(x, afnumpy.ndarray): 9 | return x.astype(cls) 10 | elif isinstance(x, numbers.Number): 11 | return numpy.int64(x) 12 | else: 13 | return afnumpy.array(x).astype(cls) 14 | 15 | class int32(numpy.int32): 16 | def __new__(cls, x=0): 17 | if isinstance(x, afnumpy.ndarray): 18 | return x.astype(cls) 19 | elif isinstance(x, numbers.Number): 20 | return numpy.int32(x) 21 | else: 22 | return afnumpy.array(x).astype(cls) 23 | 24 | class int16(numpy.intc): 25 | def __new__(cls, x=0): 26 | if isinstance(x, afnumpy.ndarray): 27 | return x.astype(cls) 28 | elif isinstance(x, numbers.Number): 29 | return numpy.int16(x) 30 | else: 31 | return afnumpy.array(x).astype(cls) 32 | 33 | class int8(numpy.int8): 34 | def __new__(cls, x=0): 35 | if isinstance(x, afnumpy.ndarray): 36 | return x.astype(cls) 37 | elif isinstance(x, numbers.Number): 38 | return numpy.int8(x) 39 | else: 40 | return afnumpy.array(x).astype(cls) 41 | 42 | 43 | class uint64(numpy.uint64): 44 | def __new__(cls, x=0): 45 | if isinstance(x, afnumpy.ndarray): 46 | return x.astype(cls) 47 | elif isinstance(x, numbers.Number): 48 | return numpy.uint64(x) 49 | else: 50 | return afnumpy.array(x).astype(cls) 51 | 52 | class uint32(numpy.uint32): 53 | def __new__(cls, x=0): 54 | if isinstance(x, afnumpy.ndarray): 55 | return x.astype(cls) 56 | elif isinstance(x, numbers.Number): 57 | return numpy.uint32(x) 58 | else: 59 | return afnumpy.array(x).astype(cls) 60 | 61 | class uint16(numpy.uint16): 62 | def __new__(cls, x=0): 63 | if isinstance(x, afnumpy.ndarray): 64 | return x.astype(cls) 65 | elif isinstance(x, numbers.Number): 66 | return numpy.uint16(x) 67 | else: 68 | return afnumpy.array(x).astype(cls) 69 | 70 | class uint8(numpy.uint8): 71 | def __new__(cls, x=0): 72 | if isinstance(x, afnumpy.ndarray): 73 | return x.astype(cls) 74 | elif isinstance(x, numbers.Number): 75 | return numpy.uint8(x) 76 | else: 77 | return afnumpy.array(x).astype(cls) 78 | 79 | 80 | class intc(numpy.intc): 81 | def __new__(cls, x=0): 82 | if isinstance(x, afnumpy.ndarray): 83 | return x.astype(cls) 84 | elif isinstance(x, numbers.Number): 85 | return numpy.intc(x) 86 | else: 87 | return afnumpy.array(x).astype(cls) 88 | 89 | class intp(numpy.intp): 90 | def __new__(cls, x=0): 91 | if isinstance(x, afnumpy.ndarray): 92 | return x.astype(cls) 93 | elif isinstance(x, numbers.Number): 94 | return numpy.intp(x) 95 | else: 96 | return afnumpy.array(x).astype(cls) 97 | 98 | class int_(numpy.int_): 99 | def __new__(cls, x=0): 100 | if isinstance(x, afnumpy.ndarray): 101 | return x.astype(cls) 102 | elif isinstance(x, numbers.Number): 103 | return numpy.int_(x) 104 | else: 105 | return afnumpy.array(x).astype(cls) 106 | 107 | class bool_(numpy.bool_): 108 | def __new__(cls, x=0): 109 | if isinstance(x, afnumpy.ndarray): 110 | return x.astype(cls) 111 | elif isinstance(x, numbers.Number): 112 | return numpy.bool_(x) 113 | else: 114 | return afnumpy.array(x).astype(cls) 115 | 116 | class float_(numpy.float_): 117 | def __new__(cls, x=0): 118 | if isinstance(x, afnumpy.ndarray): 119 | return x.astype(cls) 120 | elif isinstance(x, numbers.Number): 121 | return numpy.float_(x) 122 | else: 123 | return afnumpy.array(x).astype(cls) 124 | 125 | class float16(numpy.float16): 126 | def __new__(cls, x=0): 127 | if isinstance(x, afnumpy.ndarray): 128 | return x.astype(cls) 129 | elif isinstance(x, numbers.Number): 130 | return numpy.float16(x) 131 | else: 132 | return afnumpy.array(x).astype(cls) 133 | 134 | class float32(numpy.float32): 135 | def __new__(cls, x=0): 136 | if isinstance(x, afnumpy.ndarray): 137 | return x.astype(cls) 138 | elif isinstance(x, numbers.Number): 139 | return numpy.float32(x) 140 | else: 141 | return afnumpy.array(x).astype(cls) 142 | 143 | class float64(numpy.float64): 144 | def __new__(cls, x=0): 145 | if isinstance(x, afnumpy.ndarray): 146 | return x.astype(cls) 147 | elif isinstance(x, numbers.Number): 148 | return numpy.float64(x) 149 | else: 150 | return afnumpy.array(x).astype(cls) 151 | 152 | # removed for now 153 | #class float128(numpy.float64): 154 | # def __new__(cls, x=0): 155 | # if isinstance(x, afnumpy.ndarray): 156 | # raise NotImplementedError('Arrayfire does not support 128 bit floats') 157 | # elif isinstance(x, numbers.Number): 158 | # return numpy.float64(x) 159 | # else: 160 | # raise NotImplementedError('Arrayfire does not support 128 bit floats') 161 | 162 | class complex_(numpy.complex_): 163 | def __new__(cls, x=0): 164 | if isinstance(x, afnumpy.ndarray): 165 | return x.astype(cls) 166 | elif isinstance(x, numbers.Number): 167 | return numpy.complex_(x) 168 | else: 169 | return afnumpy.array(x).astype(cls) 170 | 171 | class complex64(numpy.complex64): 172 | def __new__(cls, x=0): 173 | if isinstance(x, afnumpy.ndarray): 174 | return x.astype(cls) 175 | elif isinstance(x, numbers.Number): 176 | return numpy.complex64(x) 177 | else: 178 | return afnumpy.array(x).astype(cls) 179 | 180 | 181 | class complex128(numpy.complex128): 182 | def __new__(cls, x=0): 183 | if isinstance(x, afnumpy.ndarray): 184 | return x.astype(cls) 185 | elif isinstance(x, numbers.Number): 186 | return numpy.complex128(x) 187 | else: 188 | return afnumpy.array(x).astype(cls) 189 | 190 | float = float 191 | complex = complex 192 | bool = bool 193 | int = int 194 | long = int 195 | bool8 = bool_ 196 | 197 | promote_types = numpy.promote_types 198 | -------------------------------------------------------------------------------- /afnumpy/sparse/csr.py: -------------------------------------------------------------------------------- 1 | from .base import spmatrix,isspmatrix 2 | from .sputils import isshape 3 | import arrayfire 4 | import afnumpy as afnp 5 | from .. import private_utils as pu 6 | 7 | class csr_matrix(spmatrix): 8 | """ 9 | Compressed Sparse Row matrix 10 | 11 | This can be instantiated in several ways: 12 | csr_matrix(D) 13 | with a dense matrix or rank-2 ndarray D 14 | 15 | csr_matrix(S) 16 | with another sparse matrix S (equivalent to S.tocsr()) 17 | 18 | csr_matrix((M, N), [dtype]) 19 | to construct an empty matrix with shape (M, N) 20 | dtype is optional, defaulting to dtype='d'. 21 | 22 | csr_matrix((data, (row_ind, col_ind)), [shape=(M, N)]) 23 | where ``data``, ``row_ind`` and ``col_ind`` satisfy the 24 | relationship ``a[row_ind[k], col_ind[k]] = data[k]``. 25 | 26 | csr_matrix((data, indices, indptr), [shape=(M, N)]) 27 | is the standard CSR representation where the column indices for 28 | row i are stored in ``indices[indptr[i]:indptr[i+1]]`` and their 29 | corresponding values are stored in ``data[indptr[i]:indptr[i+1]]``. 30 | If the shape parameter is not supplied, the matrix dimensions 31 | are inferred from the index arrays. 32 | 33 | Attributes 34 | ---------- 35 | dtype : dtype 36 | Data type of the matrix 37 | shape : 2-tuple 38 | Shape of the matrix 39 | ndim : int 40 | Number of dimensions (this is always 2) 41 | nnz 42 | Number of nonzero elements 43 | data 44 | CSR format data array of the matrix 45 | indices 46 | CSR format index array of the matrix 47 | indptr 48 | CSR format index pointer array of the matrix 49 | has_sorted_indices 50 | Whether indices are sorted 51 | 52 | Notes 53 | ----- 54 | 55 | Sparse matrices can be used in arithmetic operations: they support 56 | addition, subtraction, multiplication, division, and matrix power. 57 | 58 | Advantages of the CSR format 59 | - efficient arithmetic operations CSR + CSR, CSR * CSR, etc. 60 | - efficient row slicing 61 | - fast matrix vector products 62 | 63 | Disadvantages of the CSR format 64 | - slow column slicing operations (consider CSC) 65 | - changes to the sparsity structure are expensive (consider LIL or DOK) 66 | 67 | Examples 68 | -------- 69 | 70 | >>> import numpy as np 71 | >>> from scipy.sparse import csr_matrix 72 | >>> csr_matrix((3, 4), dtype=np.int8).toarray() 73 | array([[0, 0, 0, 0], 74 | [0, 0, 0, 0], 75 | [0, 0, 0, 0]], dtype=int8) 76 | 77 | >>> row = np.array([0, 0, 1, 2, 2, 2]) 78 | >>> col = np.array([0, 2, 2, 0, 1, 2]) 79 | >>> data = np.array([1, 2, 3, 4, 5, 6]) 80 | >>> csr_matrix((data, (row, col)), shape=(3, 3)).toarray() 81 | array([[1, 0, 2], 82 | [0, 0, 3], 83 | [4, 5, 6]]) 84 | 85 | >>> indptr = np.array([0, 2, 3, 6]) 86 | >>> indices = np.array([0, 2, 2, 0, 1, 2]) 87 | >>> data = np.array([1, 2, 3, 4, 5, 6]) 88 | >>> csr_matrix((data, indices, indptr), shape=(3, 3)).toarray() 89 | array([[1, 0, 2], 90 | [0, 0, 3], 91 | [4, 5, 6]]) 92 | 93 | As an example of how to construct a CSR matrix incrementally, 94 | the following snippet builds a term-document matrix from texts: 95 | 96 | >>> docs = [["hello", "world", "hello"], ["goodbye", "cruel", "world"]] 97 | >>> indptr = [0] 98 | >>> indices = [] 99 | >>> data = [] 100 | >>> vocabulary = {} 101 | >>> for d in docs: 102 | ... for term in d: 103 | ... index = vocabulary.setdefault(term, len(vocabulary)) 104 | ... indices.append(index) 105 | ... data.append(1) 106 | ... indptr.append(len(indices)) 107 | ... 108 | >>> csr_matrix((data, indices, indptr), dtype=int).toarray() 109 | array([[2, 1, 0, 0], 110 | [0, 1, 1, 1]]) 111 | 112 | """ 113 | format = 'csr' 114 | 115 | def __init__(self, arg1, shape=None, dtype=None, copy=False): 116 | spmatrix.__init__(self) 117 | 118 | if isspmatrix(arg1): 119 | raise ValueError("Copying another sparse matrix not yet implemented") 120 | 121 | elif isinstance(arg1, tuple): 122 | if isshape(arg1): 123 | raise ValueError("Empty sparse matrices not implemented") 124 | # It's a tuple of matrix dimensions (M, N) 125 | # create empty matrix 126 | # self._shape = check_shape(arg1) 127 | else: 128 | if len(arg1) == 2: 129 | try: 130 | obj, (row, col) = arg1 131 | except (TypeError, ValueError): 132 | raise TypeError('invalid input format') 133 | if shape is None: 134 | raise ValueError("shape parameter must not be None") 135 | obj = afnp.asarray(obj) 136 | # Arrayfire only supports floating point values 137 | if obj.dtype.kind != 'c' and obj.dtype.kind != 'f': 138 | obj = obj.astype(afnp.float32) 139 | self.dtype = obj.dtype 140 | self.d_array = arrayfire.sparse.create_sparse(afnp.asarray(obj).d_array, 141 | afnp.asarray(row).astype(afnp.int32).d_array, 142 | afnp.asarray(col).astype(afnp.int32).d_array, 143 | shape[0], shape[1], storage = arrayfire.STORAGE.COO) 144 | self.d_array = arrayfire.convert_sparse(self.d_array, arrayfire.STORAGE.CSR) 145 | self._shape = shape 146 | elif len(arg1) == 3: 147 | raise ValueError("(data, indices, indptr) format not implemented") 148 | else: 149 | raise ValueError("unrecognized %s_matrix constructor usage" % 150 | self.format) 151 | 152 | else: 153 | # must be dense 154 | try: 155 | arg1 = afnp.asarray(arg1) 156 | except: 157 | raise ValueError("unrecognized %s_matrix constructor usage" % 158 | self.format) 159 | d_array = arrayfire.sparse.create_sparse_from_dense(arg1) 160 | 161 | def __mul__(self, other): 162 | other = afnp.asarray(other).astype(self.dtype) 163 | s = arrayfire.matmul(self.d_array, other.d_array) 164 | a = afnp.ndarray(pu.af_shape(s), dtype=pu.typemap(s.dtype()), af_array=s) 165 | a._eval() 166 | return a -------------------------------------------------------------------------------- /afnumpy/linalg/linalg.py: -------------------------------------------------------------------------------- 1 | import afnumpy 2 | import arrayfire 3 | import numpy 4 | from numpy.core import complexfloating, Inf, longdouble 5 | from afnumpy import asarray, sqrt, abs 6 | from afnumpy.lib import asfarray 7 | from .. import private_utils as pu 8 | from ..decorators import * 9 | 10 | def isComplexType(t): 11 | return issubclass(t, complexfloating) 12 | 13 | def vdot(a, b): 14 | s = arrayfire.dot(arrayfire.conjg(a.flat.d_array), b.flat.d_array) 15 | return afnumpy.ndarray((), dtype=a.dtype, af_array=s)[()] 16 | 17 | @outufunc 18 | def dot(a, b): 19 | # Arrayfire requires that the types match for dot and matmul 20 | res_dtype = numpy.result_type(a,b) 21 | a = a.astype(res_dtype, copy=False) 22 | b = b.astype(res_dtype, copy=False) 23 | if a.ndim == 1 and b.ndim == 1: 24 | s = arrayfire.dot(a.d_array, b.d_array) 25 | return afnumpy.ndarray((), dtype=a.dtype, af_array=s)[()] 26 | 27 | a_shape = a.shape 28 | b_shape = b.shape 29 | if a.ndim == 1: 30 | a = a.reshape((a.shape[0],1)) 31 | if b.ndim == 1: 32 | b = b.reshape((b.shape[0],1)) 33 | 34 | if a.ndim == 2 and b.ndim == 2: 35 | # Notice the order of the arguments to matmul. It's not a bug! 36 | s = arrayfire.matmul(b.d_array, a.d_array) 37 | return afnumpy.ndarray(pu.af_shape(s), dtype=pu.typemap(s.dtype()), 38 | af_array=s) 39 | 40 | # Multidimensional dot is done with loops 41 | 42 | # Calculate the shape of the result array 43 | a_shape = list(a_shape) 44 | a_shape.pop(-1) 45 | b_shape = list(b_shape) 46 | b_shape.pop(-2) 47 | res_shape = a_shape + b_shape 48 | 49 | # Make sure the arrays are at least 3D 50 | if a.ndim < 3: 51 | a = a.reshape((1,)+a.shape) 52 | if b.ndim < 3: 53 | b = b.reshape((1,)+b.shape) 54 | 55 | # We're going to flatten the regions over which we're going to loop over 56 | # to make our life easier and reduce the amount of indexing code 57 | a = a.reshape((-1,a.shape[-2],a.shape[-1])) 58 | b = b.reshape((-1,b.shape[-2],b.shape[-1])) 59 | 60 | # Initialize the output array. The shape matches the reshape a and b. 61 | res = afnumpy.empty((a.shape[0], a.shape[-2], b.shape[0], 62 | b.shape[-1]), dtype=a.dtype) 63 | 64 | # Loop through the flattened indices and calculate the matmuls 65 | for i in range(0,a.shape[0]): 66 | for j in range(0,b.shape[0]): 67 | res[i,:,j,:] = afnumpy.dot(a[i],b[j]) 68 | 69 | # Finally appropriately reshape the result array 70 | return res.reshape(res_shape) 71 | 72 | def norm(x, ord=None, axis=None, keepdims=False): 73 | x = asarray(x) 74 | 75 | # Check the default case first and handle it immediately. 76 | if ord is None and axis is None: 77 | ndim = x.ndim 78 | x = x.ravel(order='K') 79 | if isComplexType(x.dtype.type): 80 | sqnorm = dot(x.real, x.real) + dot(x.imag, x.imag) 81 | else: 82 | sqnorm = dot(x, x) 83 | ret = sqrt(sqnorm) 84 | if keepdims: 85 | ret = ret.reshape(ndim*[1]) 86 | return ret 87 | 88 | # Normalize the `axis` argument to a tuple. 89 | nd = x.ndim 90 | if axis is None: 91 | axis = tuple(range(nd)) 92 | elif not isinstance(axis, tuple): 93 | try: 94 | axis = int(axis) 95 | except: 96 | raise TypeError("'axis' must be None, an integer or a tuple of integers") 97 | axis = (axis,) 98 | 99 | if len(axis) == 1: 100 | if ord == Inf: 101 | return abs(x).max(axis=axis, keepdims=keepdims) 102 | elif ord == -Inf: 103 | return abs(x).min(axis=axis, keepdims=keepdims) 104 | elif ord == 0: 105 | # Zero norm 106 | return (x != 0).sum(axis=axis, keepdims=keepdims) 107 | elif ord == 1: 108 | # special case for speedup 109 | return afnumpy.sum(abs(x), axis=axis, keepdims=keepdims) 110 | elif ord is None or ord == 2: 111 | # special case for speedup 112 | s = (x.conj() * x).real 113 | return sqrt(afnumpy.sum(s, axis=axis, keepdims=keepdims)) 114 | else: 115 | try: 116 | ord + 1 117 | except TypeError: 118 | raise ValueError("Invalid norm order for vectors.") 119 | if x.dtype.type is longdouble: 120 | # Convert to a float type, so integer arrays give 121 | # float results. Don't apply asfarray to longdouble arrays, 122 | # because it will downcast to float64. 123 | absx = abs(x) 124 | else: 125 | absx = x if isComplexType(x.dtype.type) else asfarray(x) 126 | if absx.dtype is x.dtype: 127 | absx = abs(absx) 128 | else: 129 | # if the type changed, we can safely overwrite absx 130 | abs(absx, out=absx) 131 | absx **= ord 132 | return afnumpy.sum(absx, axis=axis, keepdims=keepdims) ** (1.0 / ord) 133 | elif len(axis) == 2: 134 | row_axis, col_axis = axis 135 | if not (-nd <= row_axis < nd and -nd <= col_axis < nd): 136 | raise ValueError('Invalid axis %r for an array with shape %r' % 137 | (axis, x.shape)) 138 | if row_axis % nd == col_axis % nd: 139 | raise ValueError('Duplicate axes given.') 140 | if ord == 2: 141 | ret = _multi_svd_norm(x, row_axis, col_axis, amax) 142 | elif ord == -2: 143 | ret = _multi_svd_norm(x, row_axis, col_axis, amin) 144 | elif ord == 1: 145 | if col_axis > row_axis: 146 | col_axis -= 1 147 | ret = afnumpy.sum(abs(x), axis=row_axis).max(axis=col_axis) 148 | elif ord == Inf: 149 | if row_axis > col_axis: 150 | row_axis -= 1 151 | ret = afnumpy.sum(abs(x), axis=col_axis).max(axis=row_axis) 152 | elif ord == -1: 153 | if col_axis > row_axis: 154 | col_axis -= 1 155 | ret = afnumpy.sum(abs(x), axis=row_axis).min(axis=col_axis) 156 | elif ord == -Inf: 157 | if row_axis > col_axis: 158 | row_axis -= 1 159 | ret = afnumpy.sum(abs(x), axis=col_axis).min(axis=row_axis) 160 | elif ord in [None, 'fro', 'f']: 161 | ret = sqrt(afnumpy.sum((x.conj() * x).real, axis=axis)) 162 | else: 163 | raise ValueError("Invalid norm order for matrices.") 164 | if keepdims: 165 | ret_shape = list(x.shape) 166 | ret_shape[axis[0]] = 1 167 | ret_shape[axis[1]] = 1 168 | ret = ret.reshape(ret_shape) 169 | return ret 170 | else: 171 | raise ValueError("Improper number of dimensions to norm.") 172 | 173 | -------------------------------------------------------------------------------- /afnumpy/sparse/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | ===================================== 3 | Sparse matrices (:mod:`scipy.sparse`) 4 | ===================================== 5 | 6 | .. currentmodule:: scipy.sparse 7 | 8 | SciPy 2-D sparse matrix package for numeric data. 9 | 10 | Contents 11 | ======== 12 | 13 | Sparse matrix classes 14 | --------------------- 15 | 16 | .. autosummary:: 17 | :toctree: generated/ 18 | 19 | bsr_matrix - Block Sparse Row matrix 20 | coo_matrix - A sparse matrix in COOrdinate format 21 | csc_matrix - Compressed Sparse Column matrix 22 | csr_matrix - Compressed Sparse Row matrix 23 | dia_matrix - Sparse matrix with DIAgonal storage 24 | dok_matrix - Dictionary Of Keys based sparse matrix 25 | lil_matrix - Row-based linked list sparse matrix 26 | spmatrix - Sparse matrix base class 27 | 28 | Functions 29 | --------- 30 | 31 | Building sparse matrices: 32 | 33 | .. autosummary:: 34 | :toctree: generated/ 35 | 36 | eye - Sparse MxN matrix whose k-th diagonal is all ones 37 | identity - Identity matrix in sparse format 38 | kron - kronecker product of two sparse matrices 39 | kronsum - kronecker sum of sparse matrices 40 | diags - Return a sparse matrix from diagonals 41 | spdiags - Return a sparse matrix from diagonals 42 | block_diag - Build a block diagonal sparse matrix 43 | tril - Lower triangular portion of a matrix in sparse format 44 | triu - Upper triangular portion of a matrix in sparse format 45 | bmat - Build a sparse matrix from sparse sub-blocks 46 | hstack - Stack sparse matrices horizontally (column wise) 47 | vstack - Stack sparse matrices vertically (row wise) 48 | rand - Random values in a given shape 49 | random - Random values in a given shape 50 | 51 | Save and load sparse matrices: 52 | 53 | .. autosummary:: 54 | :toctree: generated/ 55 | 56 | save_npz - Save a sparse matrix to a file using ``.npz`` format. 57 | load_npz - Load a sparse matrix from a file using ``.npz`` format. 58 | 59 | Sparse matrix tools: 60 | 61 | .. autosummary:: 62 | :toctree: generated/ 63 | 64 | find 65 | 66 | Identifying sparse matrices: 67 | 68 | .. autosummary:: 69 | :toctree: generated/ 70 | 71 | issparse 72 | isspmatrix 73 | isspmatrix_csc 74 | isspmatrix_csr 75 | isspmatrix_bsr 76 | isspmatrix_lil 77 | isspmatrix_dok 78 | isspmatrix_coo 79 | isspmatrix_dia 80 | 81 | Submodules 82 | ---------- 83 | 84 | .. autosummary:: 85 | :toctree: generated/ 86 | 87 | csgraph - Compressed sparse graph routines 88 | linalg - sparse linear algebra routines 89 | 90 | Exceptions 91 | ---------- 92 | 93 | .. autosummary:: 94 | :toctree: generated/ 95 | 96 | SparseEfficiencyWarning 97 | SparseWarning 98 | 99 | 100 | Usage information 101 | ================= 102 | 103 | There are seven available sparse matrix types: 104 | 105 | 1. csc_matrix: Compressed Sparse Column format 106 | 2. csr_matrix: Compressed Sparse Row format 107 | 3. bsr_matrix: Block Sparse Row format 108 | 4. lil_matrix: List of Lists format 109 | 5. dok_matrix: Dictionary of Keys format 110 | 6. coo_matrix: COOrdinate format (aka IJV, triplet format) 111 | 7. dia_matrix: DIAgonal format 112 | 113 | To construct a matrix efficiently, use either dok_matrix or lil_matrix. 114 | The lil_matrix class supports basic slicing and fancy indexing with a 115 | similar syntax to NumPy arrays. As illustrated below, the COO format 116 | may also be used to efficiently construct matrices. Despite their 117 | similarity to NumPy arrays, it is **strongly discouraged** to use NumPy 118 | functions directly on these matrices because NumPy may not properly convert 119 | them for computations, leading to unexpected (and incorrect) results. If you 120 | do want to apply a NumPy function to these matrices, first check if SciPy has 121 | its own implementation for the given sparse matrix class, or **convert the 122 | sparse matrix to a NumPy array** (e.g. using the `toarray()` method of the 123 | class) first before applying the method. 124 | 125 | To perform manipulations such as multiplication or inversion, first 126 | convert the matrix to either CSC or CSR format. The lil_matrix format is 127 | row-based, so conversion to CSR is efficient, whereas conversion to CSC 128 | is less so. 129 | 130 | All conversions among the CSR, CSC, and COO formats are efficient, 131 | linear-time operations. 132 | 133 | Matrix vector product 134 | --------------------- 135 | To do a vector product between a sparse matrix and a vector simply use 136 | the matrix `dot` method, as described in its docstring: 137 | 138 | >>> import numpy as np 139 | >>> from scipy.sparse import csr_matrix 140 | >>> A = csr_matrix([[1, 2, 0], [0, 0, 3], [4, 0, 5]]) 141 | >>> v = np.array([1, 0, -1]) 142 | >>> A.dot(v) 143 | array([ 1, -3, -1], dtype=int64) 144 | 145 | .. warning:: As of NumPy 1.7, `np.dot` is not aware of sparse matrices, 146 | therefore using it will result on unexpected results or errors. 147 | The corresponding dense array should be obtained first instead: 148 | 149 | >>> np.dot(A.toarray(), v) 150 | array([ 1, -3, -1], dtype=int64) 151 | 152 | but then all the performance advantages would be lost. 153 | 154 | The CSR format is specially suitable for fast matrix vector products. 155 | 156 | Example 1 157 | --------- 158 | Construct a 1000x1000 lil_matrix and add some values to it: 159 | 160 | >>> from scipy.sparse import lil_matrix 161 | >>> from scipy.sparse.linalg import spsolve 162 | >>> from numpy.linalg import solve, norm 163 | >>> from numpy.random import rand 164 | 165 | >>> A = lil_matrix((1000, 1000)) 166 | >>> A[0, :100] = rand(100) 167 | >>> A[1, 100:200] = A[0, :100] 168 | >>> A.setdiag(rand(1000)) 169 | 170 | Now convert it to CSR format and solve A x = b for x: 171 | 172 | >>> A = A.tocsr() 173 | >>> b = rand(1000) 174 | >>> x = spsolve(A, b) 175 | 176 | Convert it to a dense matrix and solve, and check that the result 177 | is the same: 178 | 179 | >>> x_ = solve(A.toarray(), b) 180 | 181 | Now we can compute norm of the error with: 182 | 183 | >>> err = norm(x-x_) 184 | >>> err < 1e-10 185 | True 186 | 187 | It should be small :) 188 | 189 | 190 | Example 2 191 | --------- 192 | 193 | Construct a matrix in COO format: 194 | 195 | >>> from scipy import sparse 196 | >>> from numpy import array 197 | >>> I = array([0,3,1,0]) 198 | >>> J = array([0,3,1,2]) 199 | >>> V = array([4,5,7,9]) 200 | >>> A = sparse.coo_matrix((V,(I,J)),shape=(4,4)) 201 | 202 | Notice that the indices do not need to be sorted. 203 | 204 | Duplicate (i,j) entries are summed when converting to CSR or CSC. 205 | 206 | >>> I = array([0,0,1,3,1,0,0]) 207 | >>> J = array([0,2,1,3,1,0,0]) 208 | >>> V = array([1,1,1,1,1,1,1]) 209 | >>> B = sparse.coo_matrix((V,(I,J)),shape=(4,4)).tocsr() 210 | 211 | This is useful for constructing finite-element stiffness and mass matrices. 212 | 213 | Further Details 214 | --------------- 215 | 216 | CSR column indices are not necessarily sorted. Likewise for CSC row 217 | indices. Use the .sorted_indices() and .sort_indices() methods when 218 | sorted indices are required (e.g. when passing data to other libraries). 219 | 220 | """ 221 | 222 | from .base import * 223 | from .csr import * -------------------------------------------------------------------------------- /afnumpy/core/numeric.py: -------------------------------------------------------------------------------- 1 | import arrayfire 2 | import numpy 3 | import afnumpy 4 | from .. import private_utils as pu 5 | from numpy import newaxis 6 | import numbers 7 | from numpy import broadcast 8 | from ..decorators import * 9 | 10 | def concatenate(arrays, axis=0): 11 | if(len(arrays) < 1): 12 | raise ValueError('need at least one array to concatenate') 13 | if(axis > 3): 14 | raise NotImplementedError('only up to 4 axis as currently supported') 15 | arr = arrays[0].d_array.copy() 16 | axis = pu.c2f(arrays[0].shape, axis) 17 | for a in arrays[1:]: 18 | arr = arrayfire.join(axis, arr, a.d_array) 19 | return afnumpy.ndarray(pu.af_shape(arr), dtype=arrays[0].dtype, af_array=arr) 20 | 21 | def roll(a, shift, axis=None): 22 | shape = a.shape 23 | if(axis is None): 24 | axis = 0 25 | a = a.flatten() 26 | axis = pu.c2f(a.shape, axis) 27 | if axis == 0: 28 | s = arrayfire.shift(a.d_array, shift, 0, 0, 0) 29 | elif axis == 1: 30 | s = arrayfire.shift(a.d_array, 0, shift, 0, 0) 31 | elif axis == 2: 32 | s = arrayfire.shift(a.d_array, 0, 0, shift, 0) 33 | elif axis == 3: 34 | s = arrayfire.shift(a.d_array, 0, 0, 0, shift) 35 | else: 36 | raise NotImplementedError 37 | return afnumpy.ndarray(a.shape, dtype=a.dtype, af_array=s).reshape(shape) 38 | 39 | def rollaxis(a, axis, start=0): 40 | n = a.ndim 41 | if axis < 0: 42 | axis += n 43 | if start < 0: 44 | start += n 45 | msg = 'rollaxis: %s (%d) must be >=0 and < %d' 46 | if not (0 <= axis < n): 47 | raise ValueError(msg % ('axis', axis, n)) 48 | if not (0 <= start < n+1): 49 | raise ValueError(msg % ('start', start, n+1)) 50 | if (axis < start): # it's been removed 51 | start -= 1 52 | if axis==start: 53 | return a 54 | axes = list(range(0, n)) 55 | axes.remove(axis) 56 | axes.insert(start, axis) 57 | return a.transpose(axes) 58 | 59 | def ones(shape, dtype=float, order='C'): 60 | b = numpy.ones(shape, dtype, order) 61 | return afnumpy.ndarray(b.shape, b.dtype, buffer=b,order=order) 62 | 63 | def reshape(a, newshape, order='C'): 64 | return a.reshape(newshape,order) 65 | 66 | def asanyarray(a, dtype=None, order=None): 67 | return afnumpy.array(a, dtype, copy=False, order=order, subok=True) 68 | 69 | def floor(x, out=None): 70 | s = arrayfire.floor(x.d_array) 71 | a = afnumpy.ndarray(x.shape, dtype=pu.typemap(s.dtype()), af_array=s) 72 | if out is not None: 73 | out[:] = a[:] 74 | return a 75 | 76 | def ceil(x, out=None): 77 | s = arrayfire.ceil(x.d_array) 78 | a = afnumpy.ndarray(x.shape, dtype=pu.typemap(s.dtype()), af_array=s) 79 | if out is not None: 80 | out[:] = a[:] 81 | return a 82 | 83 | def abs(x, out=None): 84 | if not isinstance(x, afnumpy.ndarray): 85 | return numpy.abs(x, out) 86 | a = x.__abs__() 87 | if out is not None: 88 | out[:] = a 89 | return a 90 | 91 | def asarray(a, dtype=None, order=None): 92 | if(isinstance(a, afnumpy.ndarray) and 93 | (dtype is None or dtype == a.dtype)): 94 | # special case for performance 95 | return a 96 | return afnumpy.array(a, dtype, copy=False, order=order) 97 | 98 | def ascontiguousarray(a, dtype=None): 99 | return afnumpy.array(a, dtype, copy=False, order='C', ndmin=1) 100 | 101 | def cross(a, b, axisa=-1, axisb=-1, axisc=-1, axis=None): 102 | if axis is not None: 103 | axisa, axisb, axisc = (axis,) * 3 104 | a = asarray(a) 105 | b = asarray(b) 106 | # Move working axis to the end of the shape 107 | a = rollaxis(a, axisa, a.ndim) 108 | b = rollaxis(b, axisb, b.ndim) 109 | msg = ("incompatible dimensions for cross product\n" 110 | "(dimension must be 2 or 3)") 111 | if a.shape[-1] not in (2, 3) or b.shape[-1] not in (2, 3): 112 | raise ValueError(msg) 113 | 114 | # Create the output array 115 | shape = broadcast(a[..., 0], b[..., 0]).shape 116 | if a.shape[-1] == 3 or b.shape[-1] == 3: 117 | shape += (3,) 118 | dtype = afnumpy.promote_types(a.dtype, b.dtype) 119 | cp = afnumpy.empty(shape, dtype) 120 | 121 | # create local aliases for readability 122 | a0 = a[..., 0] 123 | a1 = a[..., 1] 124 | if a.shape[-1] == 3: 125 | a2 = a[..., 2] 126 | b0 = b[..., 0] 127 | b1 = b[..., 1] 128 | if b.shape[-1] == 3: 129 | b2 = b[..., 2] 130 | if cp.ndim != 0 and cp.shape[-1] == 3: 131 | cp0 = cp[..., 0] 132 | cp1 = cp[..., 1] 133 | cp2 = cp[..., 2] 134 | 135 | if a.shape[-1] == 2: 136 | if b.shape[-1] == 2: 137 | # a0 * b1 - a1 * b0 138 | afnumpy.multiply(a0, b1, out=cp) 139 | cp -= a1 * b0 140 | if cp.ndim == 0: 141 | return cp 142 | else: 143 | # This works because we are moving the last axis 144 | return rollaxis(cp, -1, axisc) 145 | else: 146 | # cp0 = a1 * b2 - 0 (a2 = 0) 147 | # cp1 = 0 - a0 * b2 (a2 = 0) 148 | # cp2 = a0 * b1 - a1 * b0 149 | afnumpy.multiply(a1, b2, out=cp0) 150 | afnumpy.multiply(a0, b2, out=cp1) 151 | negative(cp1, out=cp1) 152 | afnumpy.multiply(a0, b1, out=cp2) 153 | cp2 -= a1 * b0 154 | elif a.shape[-1] == 3: 155 | if b.shape[-1] == 3: 156 | # cp0 = a1 * b2 - a2 * b1 157 | # cp1 = a2 * b0 - a0 * b2 158 | # cp2 = a0 * b1 - a1 * b0 159 | afnumpy.multiply(a1, b2, out=cp0) 160 | tmp = afnumpy.array(a2 * b1) 161 | cp0 -= tmp 162 | afnumpy.multiply(a2, b0, out=cp1) 163 | afnumpy.multiply(a0, b2, out=tmp) 164 | cp1 -= tmp 165 | afnumpy.multiply(a0, b1, out=cp2) 166 | afnumpy.multiply(a1, b0, out=tmp) 167 | cp2 -= tmp 168 | else: 169 | # cp0 = 0 - a2 * b1 (b2 = 0) 170 | # cp1 = a2 * b0 - 0 (b2 = 0) 171 | # cp2 = a0 * b1 - a1 * b0 172 | afnumpy.multiply(a2, b1, out=cp0) 173 | negative(cp0, out=cp0) 174 | afnumpy.multiply(a2, b0, out=cp1) 175 | afnumpy.multiply(a0, b1, out=cp2) 176 | cp2 -= a1 * b0 177 | 178 | if cp.ndim == 1: 179 | return cp 180 | else: 181 | # This works because we are moving the last axis 182 | return rollaxis(cp, -1, axisc) 183 | 184 | @outufunc 185 | def isnan(x): 186 | if not isinstance(x, afnumpy.ndarray): 187 | return numpy.isnan(x) 188 | s = arrayfire.isnan(x.d_array) 189 | return afnumpy.ndarray(x.shape, dtype=pu.typemap(s.dtype()), af_array=s) 190 | 191 | @outufunc 192 | def isinf(x): 193 | if not isinstance(x, afnumpy.ndarray): 194 | return numpy.isinf(x) 195 | s = arrayfire.isinf(x.d_array) 196 | return afnumpy.ndarray(x.shape, dtype=pu.typemap(s.dtype()), af_array=s) 197 | 198 | def full(shape, fill_value, dtype=None, order='C'): 199 | """ 200 | Return a new array of given shape and type, filled with `fill_value`. 201 | 202 | Parameters 203 | ---------- 204 | shape : int or sequence of ints 205 | Shape of the new array, e.g., ``(2, 3)`` or ``2``. 206 | fill_value : scalar 207 | Fill value. 208 | dtype : data-type, optional 209 | The desired data-type for the array The default, `None`, means 210 | `np.array(fill_value).dtype`. 211 | order : {'C', 'F'}, optional 212 | Whether to store multidimensional data in C- or Fortran-contiguous 213 | (row- or column-wise) order in memory. 214 | 215 | Returns 216 | ------- 217 | out : ndarray 218 | Array of `fill_value` with the given shape, dtype, and order. 219 | 220 | See Also 221 | -------- 222 | full_like : Return a new array with shape of input filled with value. 223 | empty : Return a new uninitialized array. 224 | ones : Return a new array setting values to one. 225 | zeros : Return a new array setting values to zero. 226 | 227 | Examples 228 | -------- 229 | >>> np.full((2, 2), np.inf) 230 | array([[ inf, inf], 231 | [ inf, inf]]) 232 | >>> np.full((2, 2), 10) 233 | array([[10, 10], 234 | [10, 10]]) 235 | 236 | """ 237 | if dtype is None: 238 | dtype = array(fill_value).dtype 239 | s = arrayfire.constant(fill_value,*shape[::-1],dtype=pu.typemap(dtype)) 240 | return afnumpy.ndarray(shape, dtype=pu.typemap(s.dtype()), af_array=s) -------------------------------------------------------------------------------- /afnumpy/core/fromnumeric.py: -------------------------------------------------------------------------------- 1 | import numpy 2 | import afnumpy 3 | import arrayfire 4 | from .. import private_utils as pu 5 | from ..decorators import * 6 | 7 | def all(a, axis=None, out=None, keepdims=False): 8 | try: 9 | return a.all(axis, out, keepdims) 10 | except AttributeError: 11 | return numpy.all(a, axis, out, keepdims) 12 | 13 | def any(a, axis=None, out=None, keepdims=False): 14 | try: 15 | return a.any(axis, out, keepdims) 16 | except AttributeError: 17 | return numpy.any(a, axis, out, keepdims) 18 | 19 | def round(a, decimals=0, out=None): 20 | try: 21 | return a.round(decimals, out) 22 | except AttributeError: 23 | return numpy.round(a, decimals, out) 24 | 25 | def take(a, indices, axis=None, out=None, mode='raise'): 26 | try: 27 | return a.take(indices, axis, out, mode) 28 | except AttributeError: 29 | return numpy.take(a, indices, axis, out, mode) 30 | 31 | def amin(a, axis=None, out=None, keepdims=False): 32 | try: 33 | return a.min(axis, out, keepdims) 34 | except AttributeError: 35 | return numpy.amin(a, axis, out, keepdims) 36 | 37 | min = amin 38 | 39 | def amax(a, axis=None, out=None, keepdims=False): 40 | try: 41 | return a.max(axis, out, keepdims) 42 | except AttributeError: 43 | return numpy.amax(a, axis, out, keepdims) 44 | 45 | max = amax 46 | 47 | def prod(a, axis=None, dtype=None, out=None, keepdims=False): 48 | try: 49 | return a.prod(axis=axis, dtype=dtype, out=out, keepdims=keepdims) 50 | except AttributeError: 51 | return numpy.prod(a, axis=axis, dtype=dtype, out=out, keepdims=keepdims) 52 | 53 | def mean(a, axis=None, dtype=None, out=None, keepdims=False): 54 | try: 55 | return a.mean(axis=axis, dtype=dtype, out=out, keepdims=keepdims) 56 | except AttributeError: 57 | return numpy.mean(a, axis=axis, dtype=dtype, out=out, keepdims=keepdims) 58 | 59 | def sum(a, axis=None, dtype=None, out=None, keepdims=False): 60 | try: 61 | return a.sum(axis=axis, dtype=dtype, out=out, keepdims=keepdims) 62 | except AttributeError: 63 | return numpy.sum(a, axis=axis, dtype=dtype, out=out, keepdims=keepdims) 64 | 65 | @outufunc 66 | def sqrt(x): 67 | if isinstance(x, afnumpy.ndarray): 68 | s = arrayfire.sqrt(x.d_array) 69 | return afnumpy.ndarray(x.shape, dtype=pu.typemap(s.dtype()), af_array=s) 70 | else: 71 | return numpy.sqrt(x) 72 | 73 | def transpose(a, axes=None): 74 | try: 75 | return a.transpose(axes) 76 | except AttributeError: 77 | return numpy.tranpose(a, axes) 78 | 79 | def squeeze(a, axis=None): 80 | try: 81 | return a.squeeze(axis) 82 | except AttributeError: 83 | return numpy.squeeze(a, axis) 84 | 85 | def argmax(a, axis=None): 86 | try: 87 | return a.argmax(axis) 88 | except AttributeError: 89 | return numpy.argmax(a, axis) 90 | 91 | def argmin(a, axis=None): 92 | try: 93 | return a.argmin(axis) 94 | except AttributeError: 95 | return numpy.argmin(a, axis) 96 | 97 | def argsort(a, axis=-1, kind='quicksort', order=None): 98 | try: 99 | return a.argsort(axis, kind, order) 100 | except AttributeError: 101 | return numpy.argsort(a, axis, kind, order) 102 | 103 | def ravel(a, order='C'): 104 | """ 105 | Return a flattened array. 106 | 107 | A 1-D array, containing the elements of the input, is returned. A copy is 108 | made only if needed. 109 | 110 | Parameters 111 | ---------- 112 | a : array_like 113 | Input array. The elements in `a` are read in the order specified by 114 | `order`, and packed as a 1-D array. 115 | order : {'C','F', 'A', 'K'}, optional 116 | The elements of `a` are read using this index order. 'C' means to 117 | index the elements in C-like order, with the last axis index changing 118 | fastest, back to the first axis index changing slowest. 'F' means to 119 | index the elements in Fortran-like index order, with the first index 120 | changing fastest, and the last index changing slowest. Note that the 'C' 121 | and 'F' options take no account of the memory layout of the underlying 122 | array, and only refer to the order of axis indexing. 'A' means to read 123 | the elements in Fortran-like index order if `a` is Fortran *contiguous* 124 | in memory, C-like order otherwise. 'K' means to read the elements in 125 | the order they occur in memory, except for reversing the data when 126 | strides are negative. By default, 'C' index order is used. 127 | 128 | Returns 129 | ------- 130 | 1d_array : ndarray 131 | Output of the same dtype as `a`, and of shape ``(a.size,)``. 132 | 133 | See Also 134 | -------- 135 | ndarray.flat : 1-D iterator over an array. 136 | ndarray.flatten : 1-D array copy of the elements of an array 137 | in row-major order. 138 | 139 | Notes 140 | ----- 141 | In C-like (row-major) order, in two dimensions, the row index varies the 142 | slowest, and the column index the quickest. This can be generalized to 143 | multiple dimensions, where row-major order implies that the index along the 144 | first axis varies slowest, and the index along the last quickest. The 145 | opposite holds for Fortran-like, or column-major, index ordering. 146 | 147 | Examples 148 | -------- 149 | It is equivalent to ``reshape(-1, order=order)``. 150 | 151 | >>> x = np.array([[1, 2, 3], [4, 5, 6]]) 152 | >>> print np.ravel(x) 153 | [1 2 3 4 5 6] 154 | 155 | >>> print x.reshape(-1) 156 | [1 2 3 4 5 6] 157 | 158 | >>> print np.ravel(x, order='F') 159 | [1 4 2 5 3 6] 160 | 161 | When ``order`` is 'A', it will preserve the array's 'C' or 'F' ordering: 162 | 163 | >>> print np.ravel(x.T) 164 | [1 4 2 5 3 6] 165 | >>> print np.ravel(x.T, order='A') 166 | [1 2 3 4 5 6] 167 | 168 | When ``order`` is 'K', it will preserve orderings that are neither 'C' 169 | nor 'F', but won't reverse axes: 170 | 171 | >>> a = np.arange(3)[::-1]; a 172 | array([2, 1, 0]) 173 | >>> a.ravel(order='C') 174 | array([2, 1, 0]) 175 | >>> a.ravel(order='K') 176 | array([2, 1, 0]) 177 | 178 | >>> a = np.arange(12).reshape(2,3,2).swapaxes(1,2); a 179 | array([[[ 0, 2, 4], 180 | [ 1, 3, 5]], 181 | [[ 6, 8, 10], 182 | [ 7, 9, 11]]]) 183 | >>> a.ravel(order='C') 184 | array([ 0, 2, 4, 1, 3, 5, 6, 8, 10, 7, 9, 11]) 185 | >>> a.ravel(order='K') 186 | array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) 187 | 188 | """ 189 | return afnumpy.asarray(a).ravel(order) 190 | 191 | def sort(a, axis=-1, kind='quicksort', order=None): 192 | try: 193 | if kind != 'quicksort': 194 | print( "sort 'kind' argument ignored" ) 195 | if order is not None: 196 | raise ValueError('order argument is not supported') 197 | if(axis is None): 198 | input = a.flatten() 199 | axis = 0 200 | else: 201 | input = a 202 | if(axis < 0): 203 | axis = a.ndim+axis 204 | s = arrayfire.sort(input.d_array, pu.c2f(input.shape, axis)) 205 | return afnumpy.ndarray(input.shape, dtype=pu.typemap(s.dtype()), af_array=s) 206 | except AttributeError: 207 | return numpy.argsort(a, axis, kind, order) 208 | 209 | def repeat(a, repeats, axis=None): 210 | """ 211 | Repeat elements of an array. 212 | 213 | Parameters 214 | ---------- 215 | a : array_like 216 | Input array. 217 | repeats : int or array of ints 218 | The number of repetitions for each element. `repeats` is broadcasted 219 | to fit the shape of the given axis. 220 | axis : int, optional 221 | The axis along which to repeat values. By default, use the 222 | flattened input array, and return a flat output array. 223 | 224 | Returns 225 | ------- 226 | repeated_array : ndarray 227 | Output array which has the same shape as `a`, except along 228 | the given axis. 229 | 230 | See Also 231 | -------- 232 | tile : Tile an array. 233 | 234 | Examples 235 | -------- 236 | >>> np.repeat(3, 4) 237 | array([3, 3, 3, 3]) 238 | >>> x = np.array([[1,2],[3,4]]) 239 | >>> np.repeat(x, 2) 240 | array([1, 1, 2, 2, 3, 3, 4, 4]) 241 | >>> np.repeat(x, 3, axis=1) 242 | array([[1, 1, 1, 2, 2, 2], 243 | [3, 3, 3, 4, 4, 4]]) 244 | >>> np.repeat(x, [1, 2], axis=0) 245 | array([[1, 2], 246 | [3, 4], 247 | [3, 4]]) 248 | 249 | """ 250 | return afnumpy.asarray(a).repeat(repeats, axis=axis) -------------------------------------------------------------------------------- /tests/test_core.py: -------------------------------------------------------------------------------- 1 | import afnumpy 2 | import afnumpy as af 3 | import numpy 4 | import numpy as np 5 | from asserts import * 6 | import pytest 7 | xfail = pytest.mark.xfail 8 | 9 | def test_floor(): 10 | b = numpy.random.random((2,3)) 11 | a = afnumpy.array(b) 12 | iassert(afnumpy.floor(a), numpy.floor(b)) 13 | 14 | b = numpy.array([1,2,3]) 15 | a = afnumpy.array(b) 16 | iassert(afnumpy.floor(a), numpy.floor(b)) 17 | 18 | def test_ceil(): 19 | b = numpy.random.random((2,3)) 20 | a = afnumpy.array(b) 21 | iassert(afnumpy.ceil(a), numpy.ceil(b)) 22 | 23 | b = numpy.array([1,2,3]) 24 | a = afnumpy.array(b) 25 | iassert(afnumpy.ceil(a), numpy.ceil(b)) 26 | 27 | def test_asanyarray(): 28 | b = numpy.random.random((2,3)) 29 | a = afnumpy.array(b) 30 | iassert(afnumpy.asanyarray(a), numpy.asanyarray(b)) 31 | # zero dim arrays not supported 32 | iassert(afnumpy.asanyarray(1), numpy.asanyarray(1)) 33 | iassert(afnumpy.asanyarray([1,2]), numpy.asanyarray([1,2])) 34 | iassert(afnumpy.asanyarray(b), numpy.asanyarray(b)) 35 | 36 | def test_reshape(): 37 | b = numpy.random.random((2,3)) 38 | a = afnumpy.array(b) 39 | iassert(a.reshape((3,2)), b.reshape((3,2))) 40 | iassert(a.reshape(6), b.reshape(6)) 41 | iassert(a.reshape((-1,2)), b.reshape((-1,2))) 42 | assert a.d_array.device_ptr() == a.reshape((-1,2)).d_array.device_ptr() 43 | 44 | b = numpy.random.random((1)) 45 | a = afnumpy.array(b) 46 | # Some empty shape reshape 47 | iassert(a.reshape(()), b.reshape(())) 48 | iassert(a.reshape([]), b.reshape([])) 49 | 50 | def test_abs(): 51 | b = numpy.random.random((2,3)) 52 | a = afnumpy.array(b) 53 | fassert(afnumpy.abs(a), numpy.abs(b)) 54 | 55 | def test_ones(): 56 | a = afnumpy.ones(3) 57 | b = numpy.ones(3) 58 | iassert(a, b) 59 | 60 | def test_roll(): 61 | b = numpy.random.random(3) 62 | a = afnumpy.array(b) 63 | fassert(afnumpy.roll(a, -1, 0), numpy.roll(b, -1, 0)) 64 | 65 | b = numpy.random.random(3) 66 | a = afnumpy.array(b) 67 | fassert(afnumpy.roll(a, 1, 0), numpy.roll(b, 1, 0)) 68 | 69 | b = numpy.random.random((2, 3)) 70 | a = afnumpy.array(b) 71 | fassert(afnumpy.roll(a, 1, 0), numpy.roll(b, 1, 0)) 72 | 73 | b = numpy.random.random((2, 3)) 74 | a = afnumpy.array(b) 75 | fassert(afnumpy.roll(a, 1, 1), numpy.roll(b, 1, 1)) 76 | 77 | b = numpy.random.random((2, 3)) 78 | a = afnumpy.array(b) 79 | fassert(afnumpy.roll(a, 2), numpy.roll(b, 2)) 80 | 81 | @xfail 82 | def test_concatenate(): 83 | b = numpy.random.random((2,3)) 84 | a = afnumpy.array(b) 85 | iassert(afnumpy.concatenate(a), numpy.concatenate(b)) 86 | iassert(afnumpy.concatenate((a,a)), numpy.concatenate((b,b))) 87 | iassert(afnumpy.concatenate((a,a),axis=1), numpy.concatenate((b,b),axis=1)) 88 | 89 | def test_round(): 90 | b = numpy.random.random((2,3)) 91 | a = afnumpy.array(b) 92 | fassert(afnumpy.round(a), numpy.round(b)) 93 | 94 | def test_take(): 95 | a = [4, 3, 5, 7, 6, 8] 96 | indices = [0, 1, 4] 97 | iassert(afnumpy.take(a, indices), numpy.take(a, indices)) 98 | b = numpy.array(a) 99 | a = afnumpy.array(a) 100 | iassert(afnumpy.take(a, indices), numpy.take(b, indices)) 101 | 102 | def test_ascontiguousarray(): 103 | a = afnumpy.random.random((2,3)) 104 | b = numpy.array(a) 105 | fassert(afnumpy.ascontiguousarray(a), numpy.ascontiguousarray(b)) 106 | 107 | def test_min(): 108 | a = afnumpy.random.random((2,3)) 109 | b = numpy.array(a) 110 | fassert(afnumpy.min(a), numpy.min(b)) 111 | fassert(afnumpy.min(a,axis=0), numpy.min(b,axis=0)) 112 | fassert(afnumpy.min(a,axis=1), numpy.min(b,axis=1)) 113 | 114 | def test_max(): 115 | a = afnumpy.random.random((2,3)) 116 | b = numpy.array(a) 117 | fassert(afnumpy.max(a), numpy.max(b)) 118 | fassert(afnumpy.max(a,axis=0), numpy.max(b,axis=0)) 119 | fassert(afnumpy.max(a,axis=1), numpy.max(b,axis=1)) 120 | 121 | def test_prod(): 122 | a = afnumpy.random.random((2,3)) 123 | b = numpy.array(a) 124 | fassert(afnumpy.prod(a), numpy.prod(b)) 125 | fassert(afnumpy.prod(a,axis=0), numpy.prod(b,axis=0)) 126 | fassert(afnumpy.prod(a,axis=1), numpy.prod(b,axis=1)) 127 | 128 | def test_mean(): 129 | a = afnumpy.random.random((2,3)) 130 | b = numpy.array(a) 131 | fassert(afnumpy.mean(a), numpy.mean(b)) 132 | fassert(afnumpy.mean(a,axis=0), numpy.mean(b,axis=0)) 133 | fassert(afnumpy.mean(a,axis=1), numpy.mean(b,axis=1)) 134 | fassert(afnumpy.mean(a,axis=(0,1)), numpy.mean(b,axis=(0,1))) 135 | 136 | def test_sqrt(): 137 | a = afnumpy.random.random((2,3)) 138 | b = numpy.array(a) 139 | fassert(afnumpy.sqrt(a), numpy.sqrt(b)) 140 | 141 | def test_dtypes(): 142 | a = afnumpy.random.random((2,3)) 143 | b = numpy.array(a) 144 | fassert(afnumpy.int32(a), numpy.int32(b)) 145 | fassert(afnumpy.complex64(a), numpy.complex64(b)) 146 | assert(afnumpy.float(a.sum()), numpy.float(b.sum())) 147 | fassert(afnumpy.complex64(b), numpy.complex64(a)) 148 | assert(type(afnumpy.complex64(b)), afnumpy.ndarray) 149 | assert(type(afnumpy.complex64([1,2,3])), afnumpy.ndarray) 150 | assert(type(afnumpy.bool8(True)), numpy.bool_) 151 | 152 | def test_transpose(): 153 | b = numpy.random.random((2,3)) 154 | a = afnumpy.array(b) 155 | iassert(a.transpose(), b.transpose()) 156 | iassert(a.transpose((0,1)), b.transpose((0,1))) 157 | iassert(a.transpose((1,0)), b.transpose((1,0))) 158 | b = numpy.random.random((2)) 159 | a = afnumpy.array(b) 160 | iassert(a.transpose(), b.transpose()) 161 | b = numpy.random.random((2,3,4)) 162 | a = afnumpy.array(b) 163 | iassert(a.transpose(), b.transpose()) 164 | iassert(a.transpose((2,0,1)), b.transpose((2,0,1))) 165 | 166 | def test_rollaxis(): 167 | b = numpy.random.random((3,4,5,6)) 168 | a = afnumpy.array(b) 169 | iassert(afnumpy.rollaxis(a, 3, 1),numpy.rollaxis(b, 3, 1)) 170 | iassert(afnumpy.rollaxis(a, 2),numpy.rollaxis(b, 2)) 171 | iassert(afnumpy.rollaxis(a, 1, 4),numpy.rollaxis(b, 1, 4)) 172 | 173 | def test_cross(): 174 | x = [1, 2, 3] 175 | y = [4, 5, 6] 176 | iassert(afnumpy.cross(x, y), numpy.cross(x, y)) 177 | 178 | def test_linspace(): 179 | iassert(afnumpy.linspace(0,10), numpy.linspace(0,10)) 180 | 181 | def test_squeeze(): 182 | x = numpy.array([[[0], [1], [2]]]) 183 | y = afnumpy.array(x) 184 | iassert(afnumpy.squeeze(y), numpy.squeeze(x)) 185 | iassert(afnumpy.squeeze(y, axis=(2,)), numpy.squeeze(x, axis=(2,))) 186 | 187 | def test_all(): 188 | iassert(afnumpy.all([[True, False], [True, True]]), 189 | numpy.all([[True, False], [True, True]])) 190 | x = numpy.array([[True, False], [True, True]]) 191 | y = afnumpy.array(x) 192 | iassert(y.all(axis=0),x.all(axis=0)) 193 | 194 | def test_any(): 195 | iassert(afnumpy.any([[True, False], [True, True]]), 196 | numpy.any([[True, False], [True, True]])) 197 | x = numpy.array([[True, False], [True, True]]) 198 | y = afnumpy.array(x) 199 | iassert(y.any(axis=0),x.any(axis=0)) 200 | 201 | def test_argmax(): 202 | a = afnumpy.arange(6).reshape((2,3)) 203 | b = numpy.array(a) 204 | iassert(afnumpy.argmax(a),numpy.argmax(b)) 205 | iassert(afnumpy.argmax(a, axis=0),numpy.argmax(b, axis=0)) 206 | b[0,1] = 5 207 | a[0,1] = 5 208 | iassert(afnumpy.argmax(a),numpy.argmax(b)) 209 | 210 | def test_argmin(): 211 | a = afnumpy.arange(6).reshape((2,3)) 212 | b = numpy.array(a) 213 | iassert(afnumpy.argmin(a),numpy.argmin(b)) 214 | iassert(afnumpy.argmin(a, axis=0),numpy.argmin(b, axis=0)) 215 | b[0,1] = 5 216 | a[0,1] = 5 217 | iassert(afnumpy.argmin(a),numpy.argmin(b)) 218 | 219 | def test_argsort(): 220 | # Sort does not support 64bit int yet 221 | x = np.array([3, 1, 2], dtype=float) 222 | y = af.array(x) 223 | iassert(af.argsort(y), np.argsort(x)) 224 | x = np.array([[0, 3], [2, 2]], dtype=float) 225 | y = af.array(x) 226 | iassert(af.argsort(y), np.argsort(x)) 227 | iassert(af.argsort(y, axis=1), np.argsort(x, axis=1)) 228 | iassert(af.argsort(y, axis=None), np.argsort(x, axis=None)) 229 | # Arrayfire at the moment can only sort along the last dimension 230 | # iassert(af.argsort(y, axis=0), np.argsort(x, axis=0)) 231 | 232 | @xfail 233 | def test_argsort_xfail(): 234 | x = np.array([[0, 3], [2, 2]], dtype=float) 235 | y = af.array(x) 236 | # Arrayfire at the moment can only sort along the last dimension 237 | iassert(af.argsort(y, axis=0), np.argsort(x, axis=0)) 238 | 239 | 240 | def test_sort(): 241 | # Sort does not support 64bit int yet 242 | x = np.array([3, 1, 2], dtype=np.int32) 243 | y = af.array(x) 244 | iassert(af.sort(y), np.sort(x)) 245 | x.sort() 246 | y.sort() 247 | iassert(y,x) 248 | x = np.array([[0, 3], [2, 2]], dtype=float) 249 | y = af.array(x) 250 | iassert(af.sort(y), np.sort(x)) 251 | iassert(af.sort(y, axis=1), np.sort(x, axis=1)) 252 | iassert(af.sort(y, axis=None), np.sort(x, axis=None)) 253 | 254 | x.sort() 255 | y.sort() 256 | iassert(y,x) 257 | 258 | x = np.array([[0, 3], [2, 2]], dtype=float) 259 | y = af.array(x) 260 | x.sort(axis=1) 261 | y.sort(axis=1) 262 | iassert(y,x) 263 | 264 | x = np.array([[0, 3], [2, 2]], dtype=float) 265 | y = af.array(x) 266 | iassert(af.sort(y,axis=None),np.sort(x,axis=None)) 267 | 268 | @xfail 269 | def test_sort_xfail(): 270 | x = np.array([[0, 3], [2, 2]], dtype=float) 271 | y = af.array(x) 272 | # Arrayfire at the moment can only sort along the last dimension 273 | iassert(af.argsort(y, axis=0), np.argsort(x, axis=0)) 274 | 275 | def test_isnan(): 276 | b = 1.0*numpy.random.randint(0,2,(2,3)) 277 | b[b == 0] = numpy.nan 278 | a = afnumpy.array(b) 279 | fassert(afnumpy.isnan(a), numpy.isnan(b)) 280 | 281 | def test_isinf(): 282 | b = 1.0*numpy.random.randint(0,2,(2,3)) 283 | b[b == 0] = numpy.inf 284 | a = afnumpy.array(b) 285 | fassert(afnumpy.isnan(a), numpy.isnan(b)) 286 | 287 | @xfail 288 | def test_concatenate_xfail(): 289 | b = np.array([[1, 2], [3, 4]]) 290 | d = np.array([[5, 6]]) 291 | a = afnumpy.array(b) 292 | c = afnumpy.array(d) 293 | fassert(afnumpy.concatenate((a,c)), numpy.concatenate((b,d))) 294 | 295 | def test_repeat(): 296 | iassert(afnumpy.repeat(3, 4), numpy.repeat(3, 4)) 297 | -------------------------------------------------------------------------------- /tests/test_lib.py: -------------------------------------------------------------------------------- 1 | import afnumpy 2 | import numpy 3 | from asserts import * 4 | import sys 5 | import pytest 6 | xfail = pytest.mark.xfail 7 | 8 | def test_copy(): 9 | b = numpy.random.random((2,3)) 10 | a = afnumpy.array(b) 11 | c = afnumpy.copy(a) 12 | d = numpy.copy(b) 13 | a[:] = 0 14 | b[:] = 0 15 | iassert(c,d) 16 | 17 | def test_meshgrid(): 18 | nx, ny = (3, 2) 19 | x2 = numpy.linspace(0, 1, nx) 20 | y2 = numpy.linspace(0, 1, ny) 21 | x1 = afnumpy.array(x2) 22 | y1 = afnumpy.array(y2) 23 | 24 | iassert(afnumpy.meshgrid(x1, y1), numpy.meshgrid(x2, y2)) 25 | 26 | def test_broadcast_arrays(): 27 | # Currently arrayfire is missing support for int64 28 | x2 = numpy.array([[1,2,3]], dtype=numpy.float32) 29 | y2 = numpy.array([[1],[2],[3]], dtype=numpy.float32) 30 | x1 = afnumpy.array(x2) 31 | y1 = afnumpy.array(y2) 32 | iassert(afnumpy.broadcast_arrays(x1, y1), numpy.broadcast_arrays(x2, y2)) 33 | x1 = afnumpy.array([2]) 34 | y1 = afnumpy.array(2) 35 | x2 = numpy.array([2]) 36 | y2 = numpy.array(2) 37 | iassert(afnumpy.broadcast_arrays(x1, y1), numpy.broadcast_arrays(x2, y2)) 38 | 39 | def test_tile(): 40 | # Currently arrayfire is missing support for int64 41 | b = numpy.array([0, 1, 2], dtype=numpy.float32) 42 | a = afnumpy.array(b) 43 | iassert(afnumpy.tile(a, 2), numpy.tile(b, 2)) 44 | iassert(afnumpy.tile(a, (2,2)), numpy.tile(b, (2,2))) 45 | iassert(afnumpy.tile(a, (2,1,2)), numpy.tile(b, (2,1,2))) 46 | 47 | def test_arccos(): 48 | a = afnumpy.random.random((2,3)) 49 | b = numpy.array(a) 50 | fassert(afnumpy.arccos(a), numpy.arccos(b)) 51 | c = afnumpy.random.random((2,3)) 52 | d = numpy.array(a) 53 | fassert(afnumpy.arccos(a, out=c), numpy.arccos(b, out=d)) 54 | fassert(c, d) 55 | 56 | def test_arcsin(): 57 | a = afnumpy.random.random((2,3)) 58 | b = numpy.array(a) 59 | fassert(afnumpy.arcsin(a), numpy.arcsin(b)) 60 | c = afnumpy.random.random((2,3)) 61 | d = numpy.array(a) 62 | fassert(afnumpy.arcsin(a, out=c), numpy.arcsin(b, out=d)) 63 | fassert(c, d) 64 | 65 | def test_arctan(): 66 | a = afnumpy.random.random((2,3)) 67 | b = numpy.array(a) 68 | fassert(afnumpy.arctan(a), numpy.arctan(b)) 69 | c = afnumpy.random.random((2,3)) 70 | d = numpy.array(a) 71 | fassert(afnumpy.arctan(a, out=c), numpy.arctan(b, out=d)) 72 | fassert(c, d) 73 | 74 | def test_arctan2(): 75 | a1 = afnumpy.random.random((2,3)) 76 | b1 = numpy.array(a1) 77 | a2 = afnumpy.random.random((2,3)) 78 | b2 = numpy.array(a2) 79 | fassert(afnumpy.arctan2(a1,a2), numpy.arctan2(b1,b2)) 80 | c = afnumpy.random.random((2,3)) 81 | d = numpy.array(c) 82 | fassert(afnumpy.arctan2(a1,a2, out=c), numpy.arctan2(b1, b2, out=d)) 83 | fassert(c, d) 84 | 85 | def test_arccosh(): 86 | # Domain for arccosh starts at 1 87 | a = afnumpy.random.random((2,3))+1 88 | b = numpy.array(a) 89 | fassert(afnumpy.arccosh(a), numpy.arccosh(b)) 90 | c = afnumpy.random.random((2,3)) 91 | d = numpy.array(a) 92 | fassert(afnumpy.arccosh(a, out=c), numpy.arccosh(b, out=d)) 93 | fassert(c, d) 94 | 95 | def test_arcsinh(): 96 | a = afnumpy.random.random((2,3)) 97 | b = numpy.array(a) 98 | fassert(afnumpy.arcsinh(a), numpy.arcsinh(b)) 99 | c = afnumpy.random.random((2,3)) 100 | d = numpy.array(a) 101 | fassert(afnumpy.arcsinh(a, out=c), numpy.arcsinh(b, out=d)) 102 | fassert(c, d) 103 | 104 | def test_arctanh(): 105 | a = afnumpy.random.random((2,3)) 106 | b = numpy.array(a) 107 | fassert(afnumpy.arctanh(a), numpy.arctanh(b)) 108 | c = afnumpy.random.random((2,3)) 109 | d = numpy.array(a) 110 | fassert(afnumpy.arctanh(a, out=c), numpy.arctanh(b, out=d)) 111 | fassert(c, d) 112 | 113 | def test_cos(): 114 | a = afnumpy.random.random((2,3)) 115 | b = numpy.array(a) 116 | fassert(afnumpy.cos(a), numpy.cos(b)) 117 | c = afnumpy.random.random((2,3)) 118 | d = numpy.array(a) 119 | fassert(afnumpy.cos(a, out=c), numpy.cos(b, out=d)) 120 | fassert(c, d) 121 | 122 | def test_sin(): 123 | a = afnumpy.random.random((2,3)) 124 | b = numpy.array(a) 125 | fassert(afnumpy.sin(a), numpy.sin(b)) 126 | c = afnumpy.random.random((2,3)) 127 | d = numpy.array(a) 128 | fassert(afnumpy.sin(a, out=c), numpy.sin(b, out=d)) 129 | fassert(c, d) 130 | 131 | def test_tan(): 132 | a = afnumpy.random.random((2,3)) 133 | b = numpy.array(a) 134 | fassert(afnumpy.tan(a), numpy.tan(b)) 135 | c = afnumpy.random.random((2,3)) 136 | d = numpy.array(a) 137 | fassert(afnumpy.tan(a, out=c), numpy.tan(b, out=d)) 138 | fassert(c, d) 139 | 140 | def test_cosh(): 141 | a = afnumpy.random.random((2,3)) 142 | b = numpy.array(a) 143 | fassert(afnumpy.cosh(a), numpy.cosh(b)) 144 | c = afnumpy.random.random((2,3)) 145 | d = numpy.array(a) 146 | fassert(afnumpy.cosh(a, out=c), numpy.cosh(b, out=d)) 147 | fassert(c, d) 148 | 149 | def test_sinh(): 150 | a = afnumpy.random.random((2,3)) 151 | b = numpy.array(a) 152 | fassert(afnumpy.sinh(a), numpy.sinh(b)) 153 | c = afnumpy.random.random((2,3)) 154 | d = numpy.array(a) 155 | fassert(afnumpy.sinh(a, out=c), numpy.sinh(b, out=d)) 156 | fassert(c, d) 157 | 158 | def test_tanh(): 159 | a = afnumpy.random.random((2,3)) 160 | b = numpy.array(a) 161 | fassert(afnumpy.tanh(a), numpy.tanh(b)) 162 | c = afnumpy.random.random((2,3)) 163 | d = numpy.array(a) 164 | fassert(afnumpy.tanh(a, out=c), numpy.tanh(b, out=d)) 165 | fassert(c, d) 166 | 167 | def test_exp(): 168 | a = afnumpy.random.random((2,3)) 169 | b = numpy.array(a) 170 | fassert(afnumpy.exp(a), numpy.exp(b)) 171 | 172 | def test_log(): 173 | a = afnumpy.random.random((2,3)) 174 | b = numpy.array(a) 175 | fassert(afnumpy.log(a), numpy.log(b)) 176 | 177 | def test_log10(): 178 | a = afnumpy.random.random((2,3)) 179 | b = numpy.array(a) 180 | fassert(afnumpy.log10(a), numpy.log10(b)) 181 | 182 | def test_real(): 183 | x = numpy.sqrt([1+0j, 0+1j]) 184 | y = afnumpy.array(x) 185 | fassert(afnumpy.real(y), numpy.real(x)) 186 | y.real[:] = 0 187 | x.real[:] = 0 188 | fassert(y, x) 189 | 190 | def test_imag(): 191 | x = numpy.sqrt([1+0j, 0+1j]) 192 | y = afnumpy.array(x) 193 | fassert(afnumpy.imag(y), numpy.imag(x)) 194 | y.real[:] = 0 195 | x.real[:] = 0 196 | fassert(y, x) 197 | 198 | def test_multiply(): 199 | a = afnumpy.random.random((2,3)) 200 | b = numpy.array(a) 201 | fassert(afnumpy.multiply(a,a), numpy.multiply(b,b)) 202 | a = afnumpy.array(2) 203 | ao = afnumpy.array(0) 204 | b = numpy.array(a) 205 | bo = numpy.array(0) 206 | fassert(afnumpy.multiply(a,a), numpy.multiply(b,b)) 207 | fassert(afnumpy.multiply(a,a, out=ao), numpy.multiply(b,b, out = bo)) 208 | fassert(ao, bo) 209 | 210 | def test_subtract(): 211 | a = afnumpy.random.random((2,3)) 212 | b = numpy.array(a) 213 | fassert(afnumpy.subtract(a,a), numpy.subtract(b,b)) 214 | a = afnumpy.array(2) 215 | ao = afnumpy.array(0) 216 | b = numpy.array(a) 217 | bo = numpy.array(0) 218 | fassert(afnumpy.subtract(a,a), numpy.subtract(b,b)) 219 | fassert(afnumpy.subtract(a,a, out=ao), numpy.subtract(b,b, out = bo)) 220 | fassert(ao, bo) 221 | 222 | def test_add(): 223 | a = afnumpy.random.random((2,3)) 224 | b = numpy.array(a) 225 | fassert(afnumpy.add(a,a), numpy.add(b,b)) 226 | a = afnumpy.array(2) 227 | ao = afnumpy.array(0) 228 | b = numpy.array(a) 229 | bo = numpy.array(0) 230 | fassert(afnumpy.add(a,a), numpy.add(b,b)) 231 | fassert(afnumpy.add(a,a, out=ao), numpy.add(b,b, out = bo)) 232 | fassert(ao, bo) 233 | 234 | def test_divide(): 235 | a = afnumpy.random.random((2,3)) 236 | b = numpy.array(a) 237 | fassert(afnumpy.divide(a,a), numpy.divide(b,b)) 238 | a = afnumpy.array(2) 239 | b = numpy.array(a) 240 | if sys.version_info >= (3, 0): 241 | ao = afnumpy.array(0.) 242 | bo = numpy.array(0.) 243 | else: 244 | ao = afnumpy.array(0) 245 | bo = numpy.array(0) 246 | fassert(afnumpy.divide(a,a), numpy.divide(b,b)) 247 | fassert(afnumpy.divide(a,a, out=ao), numpy.divide(b,b, out = bo)) 248 | fassert(ao, bo) 249 | 250 | 251 | def test_true_divide(): 252 | a = afnumpy.random.random((2,3)) 253 | b = numpy.array(a) 254 | fassert(afnumpy.true_divide(a,a), numpy.true_divide(b,b)) 255 | a = afnumpy.array(2) 256 | b = numpy.array(a) 257 | ao = afnumpy.array(0.) 258 | bo = numpy.array(0.) 259 | fassert(afnumpy.true_divide(a,a), numpy.true_divide(b,b)) 260 | fassert(afnumpy.true_divide(a,a, out=ao), numpy.true_divide(b,b, out = bo)) 261 | fassert(ao, bo) 262 | 263 | def test_floor_divide(): 264 | a = afnumpy.random.random((2,3)) 265 | b = numpy.array(a) 266 | fassert(afnumpy.floor_divide(a,a), numpy.floor_divide(b,b)) 267 | a = afnumpy.array(2) 268 | b = numpy.array(a) 269 | ao = afnumpy.array(0) 270 | bo = numpy.array(0) 271 | fassert(afnumpy.floor_divide(a,a), numpy.floor_divide(b,b)) 272 | fassert(afnumpy.floor_divide(a,a, out=ao), numpy.floor_divide(b,b, out = bo)) 273 | fassert(ao, bo) 274 | 275 | def test_angle(): 276 | a = afnumpy.random.random((2,3))+afnumpy.random.random((2,3))*1.0j 277 | b = numpy.array(a) 278 | fassert(afnumpy.angle(a), numpy.angle(b)) 279 | 280 | def test_conjugate(): 281 | a = afnumpy.random.random((2,3))+afnumpy.random.random((2,3))*1.0j 282 | b = numpy.array(a) 283 | fassert(afnumpy.conjugate(a), numpy.conjugate(b)) 284 | 285 | def test_conj(): 286 | a = afnumpy.random.random((2,3))+afnumpy.random.random((2,3))*1.0j 287 | b = numpy.array(a) 288 | fassert(afnumpy.conj(a), numpy.conj(b)) 289 | 290 | def test_percentile(): 291 | a = numpy.array([[10, 7, 4], [3, 2, 1]], dtype=numpy.float32) 292 | b = afnumpy.array(a) 293 | fassert(afnumpy.percentile(b, 50), numpy.percentile(a, 50)) 294 | fassert(afnumpy.percentile(b, 50, axis=1), numpy.percentile(a, 50, axis=1)) 295 | fassert(afnumpy.percentile(b, 50, axis=1, keepdims=True), numpy.percentile(a, 50, axis=1, keepdims=True)) 296 | 297 | @xfail 298 | def test_percentile(): 299 | a = numpy.array([[10, 7, 4], [3, 2, 1]], dtype=numpy.float32) 300 | b = afnumpy.array(a) 301 | # Again problems with sorting not being supported on the slow axis 302 | fassert(afnumpy.percentile(b, 50, axis=0), numpy.percentile(a, 50, axis=0)) 303 | 304 | def test_pad(): 305 | a = [1, 2, 3, 4, 5] 306 | b = afnumpy.array(a) 307 | iassert(afnumpy.pad(b, (2,3), 'constant', constant_values=(4, 6)), 308 | numpy.pad(a, (2,3), 'constant', constant_values=(4, 6))) 309 | iassert(afnumpy.pad(b, (2,3), 'edge'), 310 | numpy.pad(a, (2,3), 'edge')) 311 | -------------------------------------------------------------------------------- /afnumpy/indexing.py: -------------------------------------------------------------------------------- 1 | import arrayfire 2 | import sys 3 | import afnumpy 4 | from . import private_utils as pu 5 | import numbers 6 | import numpy 7 | import math 8 | 9 | def __slice_len__(idx, shape, axis): 10 | maxlen = shape[axis] 11 | # Out of bounds slices should be converted to None 12 | if(idx.stop is not None and idx.stop >= maxlen): 13 | idx.stop = None 14 | if(idx.start is not None and idx.start >= maxlen): 15 | idx.start = None 16 | 17 | if idx.step is None: 18 | step = 1 19 | else: 20 | step = idx.step 21 | if idx.start is None: 22 | if step < 0: 23 | start = maxlen-1 24 | else: 25 | start = 0 26 | else: 27 | start = idx.start 28 | if(start < 0): 29 | start += maxlen 30 | if idx.stop is None: 31 | if step < 0: 32 | end = -1 33 | else: 34 | end = maxlen 35 | else: 36 | end = idx.stop 37 | if(end < 0 and step > 0): 38 | end += maxlen 39 | if(start == end): 40 | return 0 41 | 42 | if((start-end > 0 and step > 0) or 43 | (start-end < 0 and step < 0)): 44 | return 0 45 | return int(math.ceil(float(end-start)/step)) 46 | 47 | def __slice_to_seq__(shape, idx, axis): 48 | maxlen = shape[axis] 49 | if(isinstance(idx, numbers.Number)): 50 | if idx < 0: 51 | idx = maxlen + idx 52 | if(idx >= maxlen): 53 | raise IndexError('index %d is out of bounds for axis %d with size %d' % (idx, axis, maxlen)) 54 | return idx 55 | 56 | if(isinstance(idx, afnumpy.ndarray)): 57 | return idx.d_array 58 | 59 | if not isinstance(idx, slice): 60 | return afnumpy.array(idx).d_array 61 | 62 | if idx.step is None: 63 | step = 1 64 | else: 65 | step = idx.step 66 | if idx.start is None: 67 | if step < 0: 68 | start = maxlen-1 69 | else: 70 | start = 0 71 | else: 72 | start = idx.start 73 | if(start < 0): 74 | start += maxlen 75 | if idx.stop is None: 76 | if step < 0: 77 | end = 0 78 | else: 79 | end = maxlen-1 80 | else: 81 | end = idx.stop 82 | if(end < 0): 83 | end += maxlen 84 | if step < 0: 85 | end += 1 86 | else: 87 | end -= 1 88 | # arrayfire doesn't like other steps in this case 89 | if(start == end): 90 | step = 1 91 | 92 | if((start-end > 0 and step > 0) or 93 | (start-end < 0 and step < 0)): 94 | return None 95 | return arrayfire.seq(float(start), 96 | float(end), 97 | float(step)) 98 | 99 | def __npidx_to_afidx__(idx, dim_len): 100 | if(isinstance(idx, numbers.Number)): 101 | return idx 102 | if(isinstance(idx, slice)): 103 | start = idx.start 104 | stop = idx.stop 105 | step = idx.step 106 | # Out of bounds slices should be converted to None 107 | if(stop is not None and stop >= dim_len): 108 | stop = None 109 | if(start is not None and start >= dim_len): 110 | start = None 111 | 112 | if(start is not None and start < 0): 113 | start += dim_len 114 | if(stop is not None and stop < 0): 115 | stop += dim_len 116 | if idx.step is not None and idx.step < 0: 117 | if idx.start is None: 118 | start = dim_len-1 119 | if idx.stop is None: 120 | stop = -1 121 | ret = slice(start,stop,step) 122 | if __slice_len__(ret, [dim_len], 0) <= 0: 123 | return None 124 | return ret 125 | 126 | if(not isinstance(idx, afnumpy.ndarray)): 127 | idx = afnumpy.array(idx) 128 | 129 | if(afnumpy.safe_indexing): 130 | # Check if we're going out of bounds 131 | max_index = afnumpy.arrayfire.max(idx.d_array) 132 | min_index = afnumpy.arrayfire.min(idx.d_array) 133 | if max_index >= dim_len: 134 | raise IndexError('index %d is out of bounds for axis with size %d' % (max_index, dim_len)) 135 | if min_index < 0: 136 | # Transform negative indices in positive ones 137 | idx.d_array[idx.d_array < 0] += dim_len 138 | return idx.d_array 139 | 140 | 141 | def __convert_dim__(shape, idx): 142 | # Convert numpy style indexing arguments to arrayfire style 143 | # Always returns a list 144 | # Should also return the shape of the result 145 | # Also returns the shape that the input should be reshaped to 146 | input_shape = list(shape) 147 | 148 | if not isinstance(idx, tuple): 149 | idx = (idx,) 150 | idx = list(idx) 151 | 152 | # According to http://docs.scipy.org/doc/numpy/reference/arrays.indexing.html 153 | # newaxis is an alias for 'None', and 'None' can be used in place of this with the same result. 154 | newaxis = None 155 | # Check for Ellipsis. Expand it to ':' such that idx shape matches array shape, ignoring any newaxise 156 | 157 | # We have to do this because we don't want to trigger comparisons 158 | if any(e is Ellipsis for e in idx): 159 | for axis in range(0, len(idx)): 160 | if(idx[axis] is Ellipsis): 161 | i = axis 162 | break 163 | idx.pop(i) 164 | if any(e is Ellipsis for e in idx): 165 | raise IndexError('Only a single Ellipsis allowed') 166 | while __idx_ndims__(idx)-idx.count(newaxis) < len(shape): 167 | idx.insert(i, slice(None,None,None)) 168 | 169 | # Check and remove newaxis. Store their location for final reshape 170 | newaxes = [] 171 | while newaxis in idx: 172 | newaxes.append(idx.index(newaxis)) 173 | idx.remove(newaxis) 174 | 175 | # Append enough ':' to match the dimension of the aray 176 | while __idx_ndims__(idx) < len(shape): 177 | idx.append(slice(None,None,None)) 178 | 179 | # ret = [0]*len(idx) 180 | ret = [] 181 | 182 | # Check for the number of ndarrays. Raise error if there are multiple 183 | arrays_in_idx = [] 184 | for axis in range(0,len(idx)): 185 | if isinstance(idx[axis], afnumpy.ndarray): 186 | arrays_in_idx.append(axis) 187 | if isinstance(idx[axis], numpy.ndarray): 188 | idx[axis] = afnumpy.array(idx[axis]) 189 | arrays_in_idx.append(axis) 190 | if len(arrays_in_idx) > 1: 191 | # This will fail because while multiple arrays 192 | # as indices in numpy treat the values given by 193 | # the arrays as the coordinates of the hyperslabs 194 | # to keep, arrayfire does things differently. 195 | # In arrayfire each entry of each array gets combined 196 | # with all entries of all other arrays to define the coordinate 197 | # In numpy each entry only gets combined with the corresponding 198 | # entry in the other arrays. 199 | # For example if one has [0,1],[0,1] as the two arrays for numpy 200 | # this would mean that the coordinates retrieved would be [0,0], 201 | # [1,1] while for arrayfire it would be [0,0], [0,1], [1,0], [1,1]. 202 | raise NotImplementedError('Fancy indexing with multiple arrays is not implemented') 203 | # bcast_arrays = afnumpy.broadcast_arrays(*[idx[axis] for axis in arrays_in_idx]) 204 | # for axis,bcast_array in zip(arrays_in_idx, bcast_arrays): 205 | # idx[axis] = bcast_array 206 | 207 | for axis in range(0,len(idx)): 208 | # Handle boolean arrays indexes which require a reshape 209 | # of the input array 210 | if(isinstance(idx[axis], afnumpy.ndarray) and 211 | idx[axis].ndim > 1): 212 | # Flatten the extra dimensions 213 | extra_dims = 1 214 | for i in range(1,idx[axis].ndim): 215 | extra_dims *= input_shape.pop(axis+1) 216 | input_shape[axis] *= extra_dims 217 | af_idx = __npidx_to_afidx__(idx[axis], shape[axis]) 218 | ret.insert(0,af_idx) 219 | # ret[pu.c2f(shape,axis)] = af_idx 220 | 221 | ret_shape = __index_shape__(shape, ret) 222 | 223 | # Insert new dimensions start from the end so we don't perturb other insertions 224 | for n in newaxes[::-1]: 225 | ret_shape.insert(n,1) 226 | return ret, tuple(ret_shape), tuple(input_shape) 227 | 228 | def __index_shape__(A_shape, idx, del_singleton=True): 229 | shape = [] 230 | for i in range(0,len(idx)): 231 | if(idx[i] is None): 232 | shape.append(0) 233 | elif(isinstance(idx[i],numbers.Number)): 234 | if del_singleton: 235 | # Remove dimensions indexed with a scalar 236 | continue 237 | else: 238 | shape.append(1) 239 | elif(isinstance(idx[i],arrayfire.index.Seq)): 240 | if(idx[i].s == arrayfire.af_span): 241 | shape.append(A_shape[i]) 242 | else: 243 | shape.append(idx[i].size) 244 | elif(isinstance(idx[i],slice)): 245 | shape.append(__slice_len__(idx[i], pu.c2f(A_shape), i)) 246 | elif(isinstance(idx[i], arrayfire.Array)): 247 | if idx[i].dtype() is arrayfire.Dtype.b8: 248 | shape.append(int(arrayfire.sum(idx[i]))) 249 | else: 250 | shape.append(idx[i].elements()) 251 | elif(isinstance(idx[i],arrayfire.index)): 252 | if(idx[i].isspan()): 253 | shape.append(A_shape[i]) 254 | else: 255 | af_idx = idx[i].get() 256 | if(af_idx.isBatch): 257 | raise ValueError 258 | if(af_idx.isSeq): 259 | shape.append(arrayfire.seq(af_idx.seq()).size) 260 | else: 261 | shape.append(af_idx.arr_elements()) 262 | else: 263 | raise ValueError 264 | return pu.c2f(shape) 265 | 266 | def __idx_ndims__(idx): 267 | ndims = 0 268 | for i in range(0,len(idx)): 269 | if isinstance(idx[i], afnumpy.ndarray): 270 | ndims += idx[i].ndim 271 | else: 272 | ndims += 1 273 | return ndims 274 | 275 | 276 | def __expand_dim__(shape, value, idx): 277 | # reshape value, adding size 1 dimensions, such that the dimensions of value match idx 278 | idx_shape = __index_shape__(shape, idx, False) 279 | value_shape = list(value.shape) 280 | past_one_dims = False 281 | needs_reshape = False 282 | for i in range(0, len(idx_shape)): 283 | if(len(value_shape) <= i or value_shape[i] != idx_shape[i]): 284 | if(idx_shape[i] != 1): 285 | raise ValueError 286 | else: 287 | value_shape.insert(i, 1) 288 | # We only need to reshape if we are insert 289 | # a dimension after any dimension of length > 1 290 | if(past_one_dims): 291 | needs_reshape = True 292 | elif(value_shape[i] != 1): 293 | past_one_dims = True 294 | 295 | if(len(idx_shape) != len(value_shape)): 296 | raise ValueError 297 | 298 | if(needs_reshape): 299 | return value.reshape(value_shape) 300 | else: 301 | return value 302 | -------------------------------------------------------------------------------- /afnumpy/lib/arraypad.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import afnumpy as afnp 3 | import arrayfire 4 | from .. import private_utils as pu 5 | from ..decorators import * 6 | 7 | def _slice_at_axis(shape, sl, axis): 8 | """ 9 | Construct a slice tuple the length of shape, with sl at the specified axis 10 | """ 11 | slice_tup = (slice(None),) 12 | return slice_tup * axis + (sl,) + slice_tup * (len(shape) - axis - 1) 13 | 14 | 15 | def _slice_first(shape, n, axis): 16 | """ Construct a slice tuple to take the first n elements along axis """ 17 | return _slice_at_axis(shape, slice(0, n), axis=axis) 18 | 19 | 20 | def _slice_last(shape, n, axis): 21 | """ Construct a slice tuple to take the last n elements along axis """ 22 | dim = shape[axis] # doing this explicitly makes n=0 work 23 | return _slice_at_axis(shape, slice(dim - n, dim), axis=axis) 24 | 25 | def _do_prepend(arr, pad_chunk, axis): 26 | return afnp.concatenate( 27 | (pad_chunk.astype(arr.dtype, copy=False), arr), axis=axis) 28 | 29 | def _do_append(arr, pad_chunk, axis): 30 | return afnp.concatenate( 31 | (arr, pad_chunk.astype(arr.dtype, copy=False)), axis=axis) 32 | 33 | def _prepend_const(arr, pad_amt, val, axis=-1): 34 | """ 35 | Prepend constant `val` along `axis` of `arr`. 36 | 37 | Parameters 38 | ---------- 39 | arr : ndarray 40 | Input array of arbitrary shape. 41 | pad_amt : int 42 | Amount of padding to prepend. 43 | val : scalar 44 | Constant value to use. For best results should be of type `arr.dtype`; 45 | if not `arr.dtype` will be cast to `arr.dtype`. 46 | axis : int 47 | Axis along which to pad `arr`. 48 | 49 | Returns 50 | ------- 51 | padarr : ndarray 52 | Output array, with `pad_amt` constant `val` prepended along `axis`. 53 | 54 | """ 55 | if pad_amt == 0: 56 | return arr 57 | padshape = tuple(x if i != axis else pad_amt 58 | for (i, x) in enumerate(arr.shape)) 59 | return _do_prepend(arr, afnp.full(padshape, val, dtype=arr.dtype), axis) 60 | 61 | 62 | def _append_const(arr, pad_amt, val, axis=-1): 63 | """ 64 | Append constant `val` along `axis` of `arr`. 65 | 66 | Parameters 67 | ---------- 68 | arr : ndarray 69 | Input array of arbitrary shape. 70 | pad_amt : int 71 | Amount of padding to append. 72 | val : scalar 73 | Constant value to use. For best results should be of type `arr.dtype`; 74 | if not `arr.dtype` will be cast to `arr.dtype`. 75 | axis : int 76 | Axis along which to pad `arr`. 77 | 78 | Returns 79 | ------- 80 | padarr : ndarray 81 | Output array, with `pad_amt` constant `val` appended along `axis`. 82 | 83 | """ 84 | if pad_amt == 0: 85 | return arr 86 | padshape = tuple(x if i != axis else pad_amt 87 | for (i, x) in enumerate(arr.shape)) 88 | return _do_append(arr, afnp.full(padshape, val, dtype=arr.dtype), axis) 89 | 90 | 91 | def _prepend_edge(arr, pad_amt, axis=-1): 92 | """ 93 | Prepend `pad_amt` to `arr` along `axis` by extending edge values. 94 | 95 | Parameters 96 | ---------- 97 | arr : ndarray 98 | Input array of arbitrary shape. 99 | pad_amt : int 100 | Amount of padding to prepend. 101 | axis : int 102 | Axis along which to pad `arr`. 103 | 104 | Returns 105 | ------- 106 | padarr : ndarray 107 | Output array, extended by `pad_amt` edge values appended along `axis`. 108 | 109 | """ 110 | if pad_amt == 0: 111 | return arr 112 | 113 | edge_slice = _slice_first(arr.shape, 1, axis=axis) 114 | edge_arr = arr[edge_slice] 115 | return _do_prepend(arr, edge_arr.repeat(pad_amt, axis=axis), axis) 116 | 117 | 118 | def _append_edge(arr, pad_amt, axis=-1): 119 | """ 120 | Append `pad_amt` to `arr` along `axis` by extending edge values. 121 | 122 | Parameters 123 | ---------- 124 | arr : ndarray 125 | Input array of arbitrary shape. 126 | pad_amt : int 127 | Amount of padding to append. 128 | axis : int 129 | Axis along which to pad `arr`. 130 | 131 | Returns 132 | ------- 133 | padarr : ndarray 134 | Output array, extended by `pad_amt` edge values prepended along 135 | `axis`. 136 | 137 | """ 138 | if pad_amt == 0: 139 | return arr 140 | 141 | edge_slice = _slice_last(arr.shape, 1, axis=axis) 142 | edge_arr = arr[edge_slice] 143 | return _do_append(arr, edge_arr.repeat(pad_amt, axis=axis), axis) 144 | 145 | 146 | def _normalize_shape(ndarray, shape, cast_to_int=True): 147 | """ 148 | Private function which does some checks and normalizes the possibly 149 | much simpler representations of 'pad_width', 'stat_length', 150 | 'constant_values', 'end_values'. 151 | 152 | Parameters 153 | ---------- 154 | narray : ndarray 155 | Input ndarray 156 | shape : {sequence, array_like, float, int}, optional 157 | The width of padding (pad_width), the number of elements on the 158 | edge of the narray used for statistics (stat_length), the constant 159 | value(s) to use when filling padded regions (constant_values), or the 160 | endpoint target(s) for linear ramps (end_values). 161 | ((before_1, after_1), ... (before_N, after_N)) unique number of 162 | elements for each axis where `N` is rank of `narray`. 163 | ((before, after),) yields same before and after constants for each 164 | axis. 165 | (constant,) or val is a shortcut for before = after = constant for 166 | all axes. 167 | cast_to_int : bool, optional 168 | Controls if values in ``shape`` will be rounded and cast to int 169 | before being returned. 170 | 171 | Returns 172 | ------- 173 | normalized_shape : tuple of tuples 174 | val => ((val, val), (val, val), ...) 175 | [[val1, val2], [val3, val4], ...] => ((val1, val2), (val3, val4), ...) 176 | ((val1, val2), (val3, val4), ...) => no change 177 | [[val1, val2], ] => ((val1, val2), (val1, val2), ...) 178 | ((val1, val2), ) => ((val1, val2), (val1, val2), ...) 179 | [[val , ], ] => ((val, val), (val, val), ...) 180 | ((val , ), ) => ((val, val), (val, val), ...) 181 | 182 | """ 183 | ndims = ndarray.ndim 184 | 185 | # Shortcut shape=None 186 | if shape is None: 187 | return ((None, None), ) * ndims 188 | 189 | # Convert any input `info` to a NumPy array 190 | shape_arr = np.asarray(shape) 191 | 192 | try: 193 | shape_arr = np.broadcast_to(shape_arr, (ndims, 2)) 194 | except ValueError: 195 | fmt = "Unable to create correctly shaped tuple from %s" 196 | raise ValueError(fmt % (shape,)) 197 | 198 | # Cast if necessary 199 | if cast_to_int is True: 200 | shape_arr = np.round(shape_arr).astype(int) 201 | 202 | # Convert list of lists to tuple of tuples 203 | return tuple(tuple(axis) for axis in shape_arr.tolist()) 204 | 205 | def _validate_lengths(narray, number_elements): 206 | """ 207 | Private function which does some checks and reformats pad_width and 208 | stat_length using _normalize_shape. 209 | 210 | Parameters 211 | ---------- 212 | narray : ndarray 213 | Input ndarray 214 | number_elements : {sequence, int}, optional 215 | The width of padding (pad_width) or the number of elements on the edge 216 | of the narray used for statistics (stat_length). 217 | ((before_1, after_1), ... (before_N, after_N)) unique number of 218 | elements for each axis. 219 | ((before, after),) yields same before and after constants for each 220 | axis. 221 | (constant,) or int is a shortcut for before = after = constant for all 222 | axes. 223 | 224 | Returns 225 | ------- 226 | _validate_lengths : tuple of tuples 227 | int => ((int, int), (int, int), ...) 228 | [[int1, int2], [int3, int4], ...] => ((int1, int2), (int3, int4), ...) 229 | ((int1, int2), (int3, int4), ...) => no change 230 | [[int1, int2], ] => ((int1, int2), (int1, int2), ...) 231 | ((int1, int2), ) => ((int1, int2), (int1, int2), ...) 232 | [[int , ], ] => ((int, int), (int, int), ...) 233 | ((int , ), ) => ((int, int), (int, int), ...) 234 | 235 | """ 236 | normshp = _normalize_shape(narray, number_elements) 237 | for i in normshp: 238 | chk = [1 if x is None else x for x in i] 239 | chk = [1 if x >= 0 else -1 for x in chk] 240 | if (chk[0] < 0) or (chk[1] < 0): 241 | fmt = "%s cannot contain negative values." 242 | raise ValueError(fmt % (number_elements,)) 243 | return normshp 244 | 245 | def pad(array, pad_width, mode, **kwargs): 246 | if not np.asarray(pad_width).dtype.kind == 'i': 247 | raise TypeError('`pad_width` must be of integral type.') 248 | 249 | narray = afnp.array(array) 250 | pad_width = _validate_lengths(narray, pad_width) 251 | 252 | allowedkwargs = { 253 | 'constant': ['constant_values'], 254 | 'edge': [], 255 | 'linear_ramp': ['end_values'], 256 | 'maximum': ['stat_length'], 257 | 'mean': ['stat_length'], 258 | 'median': ['stat_length'], 259 | 'minimum': ['stat_length'], 260 | 'reflect': ['reflect_type'], 261 | 'symmetric': ['reflect_type'], 262 | 'wrap': [], 263 | } 264 | 265 | kwdefaults = { 266 | 'stat_length': None, 267 | 'constant_values': 0, 268 | 'end_values': 0, 269 | 'reflect_type': 'even', 270 | } 271 | 272 | if isinstance(mode, np.compat.basestring): 273 | # Make sure have allowed kwargs appropriate for mode 274 | for key in kwargs: 275 | if key not in allowedkwargs[mode]: 276 | raise ValueError('%s keyword not in allowed keywords %s' % 277 | (key, allowedkwargs[mode])) 278 | 279 | # Set kwarg defaults 280 | for kw in allowedkwargs[mode]: 281 | kwargs.setdefault(kw, kwdefaults[kw]) 282 | 283 | # Need to only normalize particular keywords. 284 | for i in kwargs: 285 | if i == 'stat_length': 286 | kwargs[i] = _validate_lengths(narray, kwargs[i]) 287 | if i in ['end_values', 'constant_values']: 288 | kwargs[i] = _normalize_shape(narray, kwargs[i], 289 | cast_to_int=False) 290 | else: 291 | return afnumpy.array(np.pad(np.array(array),pad_width,mode,**kwargs)) 292 | 293 | # If we get here, use new padding method 294 | newmat = narray.copy() 295 | 296 | # API preserved, but completely new algorithm which pads by building the 297 | # entire block to pad before/after `arr` with in one step, for each axis. 298 | 299 | if mode == 'constant': 300 | for axis, ((pad_before, pad_after), (before_val, after_val)) \ 301 | in enumerate(zip(pad_width, kwargs['constant_values'])): 302 | newmat = _prepend_const(newmat, pad_before, before_val, axis) 303 | newmat = _append_const(newmat, pad_after, after_val, axis) 304 | else: 305 | # For the other modes we'll go with the slow mode 306 | return afnumpy.array(np.pad(np.array(array),pad_width,mode,**kwargs)) 307 | 308 | return newmat 309 | -------------------------------------------------------------------------------- /tests/test_ndarray.py: -------------------------------------------------------------------------------- 1 | import afnumpy 2 | import numpy 3 | import afnumpy as af 4 | import numpy as np 5 | from asserts import * 6 | import pytest 7 | xfail = pytest.mark.xfail 8 | 9 | def test_zeros(): 10 | a = afnumpy.zeros(3) 11 | b = numpy.zeros(3) 12 | iassert(a, b) 13 | 14 | def test_fromstring(): 15 | iassert(afnumpy.fromstring('\x01\x02', dtype=numpy.uint8),numpy.fromstring('\x01\x02', dtype=numpy.uint8)) 16 | 17 | def test_ndarray_transpose(): 18 | b = numpy.random.random((2,3)) 19 | a = afnumpy.array(b) 20 | iassert(a.transpose(), b.transpose()) 21 | iassert(a.transpose(0,1), b.transpose(0,1)) 22 | iassert(a.transpose(1,0), b.transpose(1,0)) 23 | b = numpy.random.random((2)) 24 | a = afnumpy.array(b) 25 | iassert(a.transpose(), b.transpose()) 26 | b = numpy.random.random((2,3,4)) 27 | a = afnumpy.array(b) 28 | iassert(a.transpose(), b.transpose()) 29 | iassert(a.transpose((2,0,1)), b.transpose((2,0,1))) 30 | iassert(a.transpose(2,0,1), b.transpose(2,0,1)) 31 | 32 | 33 | 34 | def test_where(): 35 | a1 = afnumpy.array([1,2,3]) 36 | b1 = numpy.array(a1) 37 | 38 | a2 = afnumpy.array([0,2,1]) 39 | b2 = numpy.array(a2) 40 | 41 | # Test where with input as indices 42 | iassert(afnumpy.where(a2, a1, a2), numpy.where(b2, b1, b2)) 43 | # Test where with input as indices 44 | iassert(afnumpy.where(a2), numpy.where(b2)) 45 | # Test where with input as booleans 46 | iassert(afnumpy.where(a2 < 2, a1, a2), numpy.where(b2 < 2, b1, b2)) 47 | # Test where with input as booleans 48 | iassert(afnumpy.where(a2 < 2), numpy.where(b2 < 2)) 49 | 50 | # And now multidimensional 51 | a1 = afnumpy.array([[1,2,3],[4,5,6]]) 52 | b1 = numpy.array(a1) 53 | 54 | a2 = afnumpy.array([[0,2,1],[1,0,1]]) 55 | b2 = numpy.array(a2) 56 | 57 | # Test where with input as indices 58 | iassert(afnumpy.where(a2, a1, a2), numpy.where(b2, b1, b2)) 59 | # Test where with input as indices 60 | iassert(afnumpy.where(a2), numpy.where(b2)) 61 | 62 | # And now multidimensional 63 | b1 = numpy.random.random((3,3,3)) > 0.5 64 | a1 = afnumpy.array(b1) 65 | 66 | # Test where with input as indices 67 | iassert(afnumpy.where(a1), numpy.where(b1)) 68 | 69 | 70 | def test_array(): 71 | a = afnumpy.array([3]) 72 | b = numpy.array([3]) 73 | iassert(a, b) 74 | 75 | a = afnumpy.array([1,2,3]) 76 | b = numpy.array([1,2,3]) 77 | iassert(a, b) 78 | 79 | a = afnumpy.array(numpy.array([1,2,3])) 80 | b = numpy.array([1,2,3]) 81 | iassert(a, b) 82 | 83 | a = afnumpy.array(numpy.array([1.,2.,3.])) 84 | b = numpy.array([1.,2.,3.]) 85 | iassert(a, b) 86 | 87 | # Try multidimensional arrays 88 | a = afnumpy.array(numpy.array([[1.,2.,3.],[4.,5.,6.]])) 89 | b = numpy.array(a) 90 | iassert(a, b) 91 | 92 | # Check for non contiguous input 93 | b = numpy.array([[1.,2.,3.],[4.,5.,6.]]).T 94 | a = afnumpy.array(b) 95 | iassert(a, b) 96 | 97 | # Check using arrayfire arrays 98 | a = afnumpy.arrayfire.randu(10,5) 99 | b = afnumpy.array(a, copy=False) 100 | c = numpy.array(a) 101 | assert(a.device_ptr() == b.d_array.device_ptr()) 102 | iassert(b,c.T) 103 | 104 | 105 | def test_binary_arithmetic(): 106 | a = afnumpy.random.rand(3) 107 | b = numpy.array(a) 108 | 109 | fassert(a+a, b+b) 110 | fassert(a+3, b+3) 111 | fassert(3+a, 3+b) 112 | 113 | fassert(a-a, b-b) 114 | fassert(a-3, b-3) 115 | fassert(3-a, 3-b) 116 | 117 | fassert(a*a, b*b) 118 | fassert(a*3, b*3) 119 | fassert(3*a, 3*b) 120 | 121 | fassert(a/a, b/b) 122 | fassert(a/3, b/3) 123 | fassert(3/a, 3/b) 124 | 125 | fassert(a**a, b**b) 126 | fassert(a**3, b**3) 127 | fassert(3**a, 3**b) 128 | 129 | fassert(a%a, b%b) 130 | fassert(a%3, b%3) 131 | fassert(3%a, 3%b) 132 | 133 | # Check for arguments of diffeernt types 134 | a = afnumpy.ones(3,dtype=numpy.uint32) 135 | b = numpy.array(a) 136 | fassert(a+3.0, b+3.0) 137 | # This is a tricky case we won't support for now 138 | # fassert(a+numpy.float32(3.0), b+numpy.float32(3.0)) 139 | fassert(3.0+a, 3.0+b) 140 | 141 | fassert(a-3.0, b-3.0) 142 | fassert(3.0-a, 3.0-b) 143 | 144 | fassert(a*3.0, b*3.0) 145 | fassert(3.0*a, 3.0*b) 146 | 147 | fassert(a/3.0, b/3.0) 148 | fassert(3.0/a, 3.0/b) 149 | fassert(3/a, 3/b) 150 | 151 | fassert(a**3.0, b**3.0) 152 | fassert(3.0**a, 3.0**b) 153 | 154 | fassert(a%3.0, b%3.0) 155 | fassert(3.0%a, 3.0%b) 156 | 157 | assert(type(numpy.float32(3)+a) == afnumpy.ndarray) 158 | 159 | def test_broadcast_binary_arithmetic(): 160 | a = afnumpy.random.rand(2,3) 161 | b = afnumpy.random.rand(2,1) 162 | c = numpy.array(a) 163 | d = numpy.array(b) 164 | fassert(a*b, c*d) 165 | a*=b 166 | c*=d 167 | fassert(a, c) 168 | fassert(a/b, c/d) 169 | a/=b 170 | c/=d 171 | fassert(a, c) 172 | fassert(a+b, c+d) 173 | a+=b 174 | c+=d 175 | fassert(a, c) 176 | fassert(a-b, c-d) 177 | a-=b 178 | c-=d 179 | fassert(a, c) 180 | 181 | def test_augmented_assignment(): 182 | a = afnumpy.random.rand(3) 183 | b = numpy.array(a) 184 | 185 | mem_before = a.d_array.device_ptr() 186 | a += a 187 | assert mem_before == a.d_array.device_ptr() 188 | b += b 189 | fassert(a, b) 190 | mem_before = a.d_array.device_ptr() 191 | a += 3 192 | assert mem_before == a.d_array.device_ptr() 193 | b += 3 194 | fassert(a, b) 195 | 196 | mem_before = a.d_array.device_ptr() 197 | a -= a 198 | assert mem_before == a.d_array.device_ptr() 199 | b -= b 200 | fassert(a, b) 201 | mem_before = a.d_array.device_ptr() 202 | a -= 3 203 | assert mem_before == a.d_array.device_ptr() 204 | b -= 3 205 | fassert(a, b) 206 | 207 | mem_before = a.d_array.device_ptr() 208 | a *= a 209 | assert mem_before == a.d_array.device_ptr() 210 | b *= b 211 | fassert(a, b) 212 | mem_before = a.d_array.device_ptr() 213 | a *= 3 214 | assert mem_before == a.d_array.device_ptr() 215 | b *= 3 216 | fassert(a, b) 217 | 218 | mem_before = a.d_array.device_ptr() 219 | a /= a 220 | assert mem_before == a.d_array.device_ptr() 221 | b /= b 222 | fassert(a, b) 223 | mem_before = a.d_array.device_ptr() 224 | a /= 3 225 | assert mem_before == a.d_array.device_ptr() 226 | b /= 3 227 | fassert(a, b) 228 | 229 | def test_unary_operators(): 230 | a = afnumpy.random.rand(3) 231 | b = numpy.array(a) 232 | fassert(-a, -b) 233 | fassert(+a, +b) 234 | b = numpy.random.randint(0,2,3).astype('bool') 235 | a = afnumpy.array(b) 236 | fassert(-a, ~b) 237 | fassert(+a, +b) 238 | # fassert(~a, ~b) 239 | 240 | def test_comparisons(): 241 | a1 = afnumpy.random.rand(3) 242 | b1 = numpy.array(a1) 243 | 244 | a2 = afnumpy.random.rand(3) 245 | b2 = numpy.array(a2) 246 | 247 | iassert(a1 > a2, b1 > b2) 248 | iassert(a1 > 0.5, b1 > 0.5) 249 | iassert(0.5 > a1, 0.5 > b1) 250 | 251 | iassert(a1 >= a2, b1 >= b2) 252 | iassert(a1 >= 0.5, b1 >= 0.5) 253 | iassert(0.5 >= a1, 0.5 >= b1) 254 | 255 | iassert(a1 < a2, b1 < b2) 256 | iassert(a1 < 0.5, b1 < 0.5) 257 | iassert(0.5 < a1, 0.5 < b1) 258 | 259 | iassert(a1 <= a2, b1 <= b2) 260 | iassert(a1 <= 0.5, b1 <= 0.5) 261 | iassert(0.5 <= a1, 0.5 <= b1) 262 | 263 | iassert(a1 == a2, b1 == b2) 264 | iassert(a1 == 0.5, b1 == 0.5) 265 | iassert(0.5 == a1, 0.5 == b1) 266 | 267 | iassert(a1 != a2, b1 != b2) 268 | iassert(a1 != 0.5, b1 != 0.5) 269 | iassert(0.5 != a1, 0.5 != b1) 270 | iassert(a1 is not None, b1 is not None) 271 | 272 | def test_ndarray_all(): 273 | b = numpy.random.randint(0,2,3).astype('bool') 274 | a = afnumpy.array(b) 275 | iassert(a.all(), b.all()) 276 | iassert(a.all(axis=0), b.all(axis=0)) 277 | 278 | b = numpy.random.randint(0,2,(3,2)).astype('bool') 279 | a = afnumpy.array(b) 280 | iassert(a.all(), b.all()) 281 | iassert(a.all(axis=0), b.all(axis=0)) 282 | iassert(a.all(keepdims=True), b.all(keepdims=True)) 283 | 284 | def test_sum(): 285 | b = numpy.random.random(3) 286 | a = afnumpy.array(b) 287 | fassert(afnumpy.sum(a), numpy.sum(b)) 288 | fassert(afnumpy.sum(a,axis=0), numpy.sum(b,axis=0)) 289 | fassert(afnumpy.sum(a,keepdims=True), numpy.sum(b,keepdims=True)) 290 | 291 | b = numpy.random.random((2,3)) 292 | a = afnumpy.array(b) 293 | fassert(afnumpy.sum(a), numpy.sum(b)) 294 | fassert(afnumpy.sum(a,axis=0), numpy.sum(b,axis=0)) 295 | 296 | def test_max(): 297 | b = numpy.random.random(3)+numpy.random.random(3)*1.0j 298 | a = afnumpy.array(b) 299 | # Arrayfire uses the magnitude for max while numpy uses 300 | # the real part as primary key followed by the imaginary part 301 | # fassert(a.max(), b.max()) 302 | b = numpy.random.random(3) 303 | a = afnumpy.array(b) 304 | fassert(a.max(), b.max()) 305 | 306 | def test_min(): 307 | b = numpy.random.random(3)+numpy.random.random(3)*1.0j 308 | a = afnumpy.array(b) 309 | # Arrayfire uses the magnitude for max while numpy uses 310 | # the real part as primary key followed by the imaginary part 311 | # fassert(a.min(), b.min()) 312 | b = numpy.random.random(3) 313 | a = afnumpy.array(b) 314 | fassert(a.min(), b.min()) 315 | 316 | def test_ndarray_abs(): 317 | b = numpy.random.random(3)+numpy.random.random(3)*1.0j 318 | a = afnumpy.array(b) 319 | fassert(abs(a), abs(b)) 320 | b = numpy.random.random(3) 321 | a = afnumpy.array(b) 322 | fassert(abs(a), abs(b)) 323 | 324 | def test_getitem(): 325 | b = numpy.random.random((3)) 326 | a = afnumpy.array(b) 327 | iassert(a[0], b[0]) 328 | iassert(a[2], b[2]) 329 | iassert(a[:], b[:]) 330 | iassert(a[0:], b[0:]) 331 | iassert(a[:-1], b[:-1]) 332 | iassert(a[0:-1], b[0:-1]) 333 | iassert(a[1:-1], b[1:-1]) 334 | iassert(a[1:2], b[1:2]) 335 | iassert(a[...,0], b[...,0]) 336 | # This will return an empty array, which is not yet supported 337 | # iassert(a[1:1], b[1:1]) 338 | iassert(a[-2:], b[-2:]) 339 | iassert(a[-3:-1], b[-3:-1]) 340 | iassert(a[1:-1:1], b[1:-1:1]) 341 | iassert(a[1:-1:2], b[1:-1:2]) 342 | iassert(a[::2], b[::2]) 343 | iassert(a[::3], b[::3]) 344 | iassert(a[::-1], b[::-1]) 345 | iassert(a[::-2], b[::-2]) 346 | iassert(a[-1::-1], b[-1::-1]) 347 | iassert(a[-1:1:-1], b[-1:1:-1]) 348 | iassert(a[-2::-1], b[-2::-1]) 349 | iassert(a[-2:0:-1], b[-2:0:-1]) 350 | iassert(a[-2::-2], b[-2::-2]) 351 | iassert(a[-2::2], b[-2::2]) 352 | iassert(a[([0],)], b[([0],)]) 353 | 354 | # Now multidimensional! 355 | b = numpy.random.random((2,3)) 356 | a = afnumpy.array(b) 357 | 358 | iassert(a[:], b[:]) 359 | iassert(a[0], b[0]) 360 | iassert(a[:,2], b[:,2]) 361 | iassert(a[1,:], b[1,:]) 362 | iassert(a[:,::-1], b[:,::-1]) 363 | 364 | # Boolean indexing 365 | d = numpy.random.random((2)) > 0.5 366 | c = afnumpy.array(d) 367 | iassert(a[c,:], b[d,:]) 368 | 369 | 370 | b = numpy.random.random((2,3,1)) 371 | a = afnumpy.array(b) 372 | iassert(a[:], b[:]) 373 | 374 | b = numpy.random.random((2,3,1,2)) 375 | a = afnumpy.array(b) 376 | iassert(a[:], b[:]) 377 | iassert(a[1,:,:,:], b[1,:,:,:]) 378 | iassert(a[1,:,0,:], b[1,:,0,:]) 379 | iassert(a[1,1,:,:], b[1,1,:,:]) 380 | d = numpy.array([0,2],dtype=numpy.int32) 381 | c = afnumpy.array(d) 382 | iassert(a[1,c,0,:], b[1,d,0,:]) 383 | 384 | # Boolean indexing 385 | d = b > 0.5 386 | c = afnumpy.array(d) 387 | iassert(a[c], b[d]) 388 | 389 | d = numpy.random.random((2,3)) > 0.5 390 | c = afnumpy.array(d) 391 | iassert(a[c,:], b[d,:]) 392 | 393 | # Zero dimensional 394 | b = numpy.ones(()) 395 | a = afnumpy.array(b) 396 | iassert(a[()],b[()]) 397 | 398 | # Partial boolean indexing 399 | b = numpy.ones((3,3)) 400 | a = afnumpy.array(b) 401 | d = numpy.ones((3)) > 0 402 | c = afnumpy.array(d) 403 | iassert(a[c],b[d]) 404 | 405 | # Partial array indexing 406 | b = numpy.ones((3,3)) 407 | a = afnumpy.array(b) 408 | d = numpy.array([0,1]) 409 | c = afnumpy.array(d) 410 | iassert(a[c],b[d]) 411 | 412 | @xfail 413 | def test_getitem_xfail(): 414 | # Slices that extend outside the array 415 | b = numpy.ones((3)) 416 | a = afnumpy.array(b) 417 | iassert(a[1:4],b[1:4]) 418 | # This case no longer works with current version of arrayfire, accessing a d_array with slice(None,-1,-1) does not give the expected output 419 | iassert(a[3::-1],b[3::-1]) 420 | 421 | def test_getitem_multi_array(): 422 | # Multidimensional array indexing 423 | b = numpy.random.random((2,2)) 424 | a = afnumpy.array(b) 425 | d = numpy.array([0,1]) 426 | c = afnumpy.array(d) 427 | iassert(a[c,c], b[d,d]) 428 | 429 | def test_newaxis(): 430 | b = numpy.random.random((3)) 431 | a = afnumpy.array(b) 432 | # iassert(a[afnumpy.newaxis,:], b[numpy.newaxis,:]) 433 | 434 | def test_setitem(): 435 | b = numpy.random.random((3)) 436 | a = afnumpy.array(b) 437 | mem_before = a.d_array.device_ptr() 438 | a[0] = 1; 439 | b[0] = 1; 440 | iassert(a, b) 441 | assert mem_before == a.d_array.device_ptr() 442 | a[:] = 2; 443 | b[:] = 2; 444 | assert mem_before == a.d_array.device_ptr() 445 | iassert(a, b) 446 | d = numpy.array([0,1],dtype=numpy.int32) 447 | c = afnumpy.array(d) 448 | a[c] = 3; 449 | b[d] = 3; 450 | assert mem_before == a.d_array.device_ptr() 451 | 452 | # Multidimensional 453 | # 2D 454 | b1 = numpy.random.random((2,2)) 455 | b2 = numpy.random.random(2) 456 | a1 = afnumpy.array(b1) 457 | a2 = afnumpy.array(b2) 458 | mem_before = a1.d_array.device_ptr() 459 | a1[:] = 1 460 | b1[:] = 1 461 | iassert(a1,b1) 462 | assert mem_before == a1.d_array.device_ptr() 463 | a1[:,0] = a2[:] 464 | b1[:,0] = b2[:] 465 | iassert(a1,b1) 466 | assert mem_before == a1.d_array.device_ptr() 467 | a1[c,0] = -a2[:] 468 | b1[d,0] = -b2[:] 469 | iassert(a1,b1) 470 | assert mem_before == a1.d_array.device_ptr() 471 | a1[0,c] = a2[:] 472 | b1[0,d] = b2[:] 473 | iassert(a1,b1) 474 | assert mem_before == a1.d_array.device_ptr() 475 | a1[0] = a2[:] 476 | b1[0] = b2[:] 477 | iassert(a1,b1) 478 | assert mem_before == a1.d_array.device_ptr() 479 | 480 | # 3D 481 | b1 = numpy.random.random((2,3,1)) 482 | b2 = numpy.random.random((3,1)) 483 | a1 = afnumpy.array(b1) 484 | a2 = afnumpy.array(b2) 485 | mem_before = a1.d_array.device_ptr() 486 | a1[0,:,:] = a2[:] 487 | b1[0,:,:] = b2[:] 488 | iassert(a1,b1) 489 | assert mem_before == a1.d_array.device_ptr() 490 | 491 | a1[0] = a2[:] 492 | b1[0] = b2[:] 493 | iassert(a1,b1) 494 | assert mem_before == a1.d_array.device_ptr() 495 | 496 | # 4D 497 | b1 = numpy.random.random((2,3,2,2)) 498 | b2 = numpy.random.random((2,2)) 499 | a1 = afnumpy.array(b1) 500 | a2 = afnumpy.array(b2) 501 | d = numpy.array([0,1],dtype=numpy.int32) 502 | c = afnumpy.array(d) 503 | mem_before = a1.d_array.device_ptr() 504 | a1[:,0,0,c] = a2 505 | b1[:,0,0,d] = b2 506 | iassert(a1,b1) 507 | assert mem_before == a1.d_array.device_ptr() 508 | 509 | a1[1,2] = a2 510 | b1[1,2] = b2 511 | iassert(a1,b1) 512 | assert mem_before == a1.d_array.device_ptr() 513 | 514 | # Boolean indexing 515 | d = b > 0.5 516 | c = afnumpy.array(d) 517 | a[c] = 1 518 | b[d] = 1 519 | iassert(a, b) 520 | a[a < 0.3] = 1 521 | b[b < 0.3] = 1 522 | iassert(a, b) 523 | 524 | # Multidimensional Boolean 525 | a1[a1 < 0.3] = 1 526 | b1[b1 < 0.3] = 1 527 | iassert(a1, b1) 528 | 529 | def test_setitem_multi_array(): 530 | # Multidimensional array indexing 531 | b = numpy.random.random((2,2)) 532 | a = afnumpy.array(b) 533 | d = numpy.array([0,1]) 534 | c = afnumpy.array(d) 535 | # This will fail because while multiple arrays 536 | # as indices in numpy treat the values given by 537 | # the arrays as the coordinates of the hyperslabs 538 | # to keep arrayfire does things differently. 539 | # In arrayfire each entry of each array gets combined 540 | # with all entries of all other arrays to define the coordinate 541 | # In numpy each entry only gets combined with the corresponding 542 | # entry in the other arrays. 543 | # For example if one has [0,1],[0,1] as the two arrays for numpy 544 | # this would mean that the coordinates retrieved would be [0,0], 545 | # [1,1] while for arrayfire it would be [0,0], [0,1], [1,0], [1,1]. 546 | a[c,c] = c 547 | b[d,d] = d 548 | iassert(a, b) 549 | 550 | def test_views(): 551 | b = numpy.random.random((3,3)) 552 | a = afnumpy.array(b) 553 | a[0] = 1 554 | b[0] = 1 555 | c = a[0] 556 | d = b[0] 557 | c[:] = 0 558 | d[:] = 0 559 | iassert(a,b) 560 | 561 | assert a[0,:].d_array.device_ptr() == a[0,:].d_array.device_ptr() 562 | # There is currently no way to get views with stride[0] > 1 563 | # assert a[:,0].d_array.device_ptr() == a[:,0].d_array.device_ptr() 564 | 565 | b = numpy.random.random((3)) 566 | a = afnumpy.array(b) 567 | c = a[...,0] 568 | assert a.d_array.device_ptr() == c.d_array.device_ptr() 569 | d = b[...,0] 570 | c[()] = 0 571 | d[()] = 0 572 | iassert(a,b) 573 | 574 | def test_ndarray_astype(): 575 | b = numpy.random.random(3) 576 | a = afnumpy.array(b) 577 | iassert(a.astype(numpy.uint8),b.astype(numpy.uint8)) 578 | iassert(a.astype(numpy.complex128),b.astype(numpy.complex128)) 579 | 580 | def test_ndarray_len(): 581 | b = numpy.random.random(3) 582 | a = afnumpy.array(b) 583 | assert(len(a) == len(b)) 584 | b = numpy.random.random((3,3)) 585 | a = afnumpy.array(b) 586 | assert(len(a) == len(b)) 587 | 588 | 589 | def test_vstack(): 590 | b = numpy.random.random((2,3)) 591 | a = afnumpy.array(b) 592 | iassert(afnumpy.vstack(a), numpy.vstack(b)) 593 | iassert(afnumpy.vstack((a,a)), numpy.vstack((b,b))) 594 | 595 | def test_hstack(): 596 | b = numpy.random.random((2,3)) 597 | a = afnumpy.array(b) 598 | iassert(afnumpy.hstack(a), numpy.hstack(b)) 599 | iassert(afnumpy.hstack((a,a)), numpy.hstack((b,b))) 600 | 601 | 602 | def test_empty_ndarray(): 603 | a = afnumpy.zeros(()) 604 | b = numpy.zeros(()) 605 | iassert(a,b) 606 | a = afnumpy.ndarray(0) 607 | b = numpy.ndarray(0) 608 | iassert(a,b) 609 | a = afnumpy.ndarray((0,)) 610 | b = numpy.ndarray((0,)) 611 | iassert(a,b) 612 | a = afnumpy.zeros(3) 613 | b = numpy.zeros(3) 614 | iassert(a[0:0],b[0:0]) 615 | 616 | 617 | 618 | def test_arange(): 619 | iassert(afnumpy.arange(10), numpy.arange(10)) 620 | iassert(afnumpy.arange(1,10), numpy.arange(1,10)) 621 | iassert(afnumpy.arange(10,1,-1), numpy.arange(10,1,-1)) 622 | iassert(afnumpy.arange(10,1,-1,dtype=numpy.int32), numpy.arange(10,1,-1,dtype=numpy.int32)) 623 | 624 | def test_ndarray_shape(): 625 | b = numpy.random.random((2,3)) 626 | a = afnumpy.array(b) 627 | a.shape = (3,2) 628 | b.shape = (3,2) 629 | fassert(a,b) 630 | 631 | 632 | def test_ndarray_round(): 633 | b = numpy.random.random((2,3)) 634 | a = afnumpy.array(b) 635 | fassert(a.round(), b.round()) 636 | 637 | def test_ndarray_take(): 638 | b = numpy.array([4, 3, 5, 7, 6, 8]) 639 | a = afnumpy.array(b) 640 | indices = [0, 1, 4] 641 | iassert(a.take(indices), b.take(indices)) 642 | b = numpy.random.random((2,3)) 643 | a = afnumpy.array(b) 644 | iassert(a.take([0,1],axis=1), b.take([0,1],axis=1)) 645 | iassert(a.take([0,1]), b.take([0,1])) 646 | 647 | def test_ndarray_min(): 648 | a = afnumpy.random.random((2,3)) 649 | b = numpy.array(a) 650 | fassert(a.min(), b.min()) 651 | fassert(a.min(axis=1), b.min(axis=1)) 652 | fassert(a.min(axis=1, keepdims=True), b.min(axis=1, keepdims=True)) 653 | 654 | def test_ndarray_max(): 655 | a = afnumpy.random.random((2,3)) 656 | b = numpy.array(a) 657 | fassert(a.max(), b.max()) 658 | fassert(a.max(axis=1), b.max(axis=1)) 659 | fassert(a.max(axis=1, keepdims=True), b.max(axis=1, keepdims=True)) 660 | 661 | def test_ndarray_sum(): 662 | a = afnumpy.random.random((2,3)) 663 | b = numpy.array(a) 664 | fassert(a.sum(), b.sum()) 665 | fassert(a.sum(axis=1), b.sum(axis=1)) 666 | fassert(a.sum(axis=1, keepdims=True), b.sum(axis=1, keepdims=True)) 667 | fassert(a.sum(axis=(0,1), keepdims=True), b.sum(axis=(0,1), keepdims=True)) 668 | fassert(a.sum(axis=(0,1)), b.sum(axis=(0,1))) 669 | a = afnumpy.random.random(()) 670 | b = afnumpy.array(a) 671 | fassert(a.sum(), b.sum()) 672 | 673 | def test_ndarray_conj(): 674 | # The weird astype is because of issue #914 in arrayfire 675 | a =afnumpy.random.random((2,3)).astype(numpy.complex64)+1.0j 676 | b = numpy.array(a) 677 | fassert(a.conj(), b.conj()) 678 | 679 | def test_empty(): 680 | a = afnumpy.empty((2,3)) 681 | b = numpy.array(a) 682 | a[:] = 1 683 | b[:] = 1 684 | fassert(a,b) 685 | 686 | def test_ndarray_T(): 687 | x = numpy.array([[1.,2.],[3.,4.]]) 688 | y = afnumpy.array(x) 689 | fassert(y.T,x.T) 690 | x = numpy.array([1.,2.,3.,4.]) 691 | y = afnumpy.array(x) 692 | fassert(y.T,x.T) 693 | 694 | def test_ndarray_any(): 695 | x = numpy.array([[True, False], [True, True]]) 696 | y = afnumpy.array(x) 697 | iassert(y.any(),x.any()) 698 | iassert(y.any(axis=0),x.any(axis=0)) 699 | 700 | def test_ndarray_real(): 701 | x = np.sqrt([1+0j, 0+1j]) 702 | y = af.array(x) 703 | fassert(y.real, x.real) 704 | y.real[:] = 0 705 | x.real[:] = 0 706 | fassert(y, x) 707 | 708 | def test_ndarray_imag(): 709 | x = np.sqrt([1+0j, 0+1j]) 710 | y = af.array(x) 711 | fassert(y.imag, x.imag) 712 | y.imag[:] = 0 713 | x.imag[:] = 0 714 | fassert(y, x) 715 | x = np.sqrt([1.0, 0.0]) 716 | y = af.array(x) 717 | fassert(y.imag, x.imag) 718 | 719 | 720 | def test_ndarray_strides(): 721 | a = afnumpy.random.random((4,3)) 722 | b = numpy.array(a) 723 | iassert(a.strides, b.strides) 724 | iassert(a[:,:].strides, b[:,:].strides) 725 | iassert(a[1:,:].strides, b[1:,:].strides) 726 | iassert(a[:,1:].strides, b[:,1:].strides) 727 | # The following cases fails for arrayfire < 3.3 as the stride 728 | # hack requires at least 2 elements per dimension 729 | iassert(a[3:,:].strides, b[3:,:].strides) 730 | iassert(a[2:,2:].strides, b[2:,2:].strides) 731 | iassert(a[3,:2].strides, b[3,:2].strides) 732 | 733 | @xfail 734 | def test_ndarray_strides_xfail(): 735 | # The following case fails as arrayfire always drops 736 | # leading dimensions of size 1 and so the stride 737 | # information is missing 738 | a = afnumpy.random.random((4,3)) 739 | b = numpy.array(a) 740 | iassert(a[3:,:2].strides, b[3:,:2].strides) 741 | 742 | def test_ndarray_copy(): 743 | b = numpy.random.random((3,3)) 744 | a = afnumpy.array(b) 745 | iassert(a.copy(), b.copy()) 746 | 747 | def test_ndarray_nonzero(): 748 | b = numpy.random.random((3,3,3)) > 0.5 749 | a = afnumpy.array(b) 750 | iassert(a.nonzero(), b.nonzero()) 751 | 752 | def test_ndarray_constructor(): 753 | a = afnumpy.arrayfire.randu(3,2) 754 | with pytest.raises(ValueError): 755 | b = afnumpy.ndarray(a.dims(), dtype='f', af_array = a) 756 | # This one should be fine 757 | b = afnumpy.ndarray(a.dims()[::-1], dtype='f', af_array = a) 758 | 759 | c = afnumpy.ndarray(a.dims()[::-1], dtype='f', buffer=a.raw_ptr(), 760 | buffer_type=afnumpy.arrayfire.get_active_backend()) 761 | 762 | d = afnumpy.ndarray(a.dims()[::-1], dtype='f', buffer=a.raw_ptr(), 763 | buffer_type=afnumpy.arrayfire.get_active_backend()) 764 | # Make sure they share the same underlying data 765 | d[0,0] = 3 766 | assert d[0,0] == c[0,0] 767 | 768 | 769 | if afnumpy.arrayfire.get_active_backend() == 'cpu': 770 | c = numpy.ones((3,2)) 771 | d = afnumpy.ndarray(c.shape, dtype=c.dtype, buffer=c.ctypes.data, 772 | buffer_type=afnumpy.arrayfire.get_active_backend()) 773 | # Make sure they share the same underlying data 774 | d[0,0] = -1 775 | assert d[0,0] == c[0,0] 776 | 777 | with pytest.raises(ValueError): 778 | # Check for wrong backend 779 | b = afnumpy.ndarray(c.shape, dtype=c.dtype, buffer=c.ctypes.data, 780 | buffer_type='cuda') 781 | 782 | def test_flatten(): 783 | b = numpy.random.random((3,3,3)) 784 | a = afnumpy.array(b) 785 | iassert(a.flatten(), b.flatten()) 786 | iassert(a.flatten(order='C'), b.flatten(order='C')) 787 | iassert(a.flatten(order='K'), b.flatten(order='K')) 788 | iassert(a.flatten(order='A'), b.flatten(order='A')) 789 | # test if it's a copy 790 | d = b.flatten() 791 | c = a.flatten() 792 | b[0] += 1 793 | a[0] += 1 794 | iassert(c.flatten(), d.flatten()) 795 | -------------------------------------------------------------------------------- /afnumpy/multiarray.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import arrayfire 3 | import sys 4 | import numpy 5 | import numbers 6 | from . import private_utils as pu 7 | import afnumpy 8 | from . import indexing 9 | from .decorators import * 10 | import collections 11 | 12 | 13 | 14 | 15 | class ndarray(object): 16 | # Ensures that our functions are called before numpy ones 17 | __array_priority__ = 20 18 | def __init__(self, shape, dtype=float, buffer=None, offset=0, strides=None, order=None, af_array=None, buffer_type='python'): 19 | self._base = None 20 | if(offset != 0): 21 | raise NotImplementedError('offset must be 0') 22 | if(strides is not None): 23 | raise NotImplementedError('strides must be None') 24 | if(order is not None and order != 'C'): 25 | raise NotImplementedError('order must be None') 26 | if isinstance(shape, numbers.Number): 27 | self._shape = (shape,) 28 | else: 29 | self._shape = tuple(shape) 30 | # Make sure you transform the type to a numpy dtype 31 | self.dtype = numpy.dtype(dtype) 32 | s_a = numpy.array(pu.c2f(shape),dtype=pu.dim_t) 33 | if(s_a.size < 1): 34 | # We'll use af_arrays of size (1) to keep scalars 35 | s_a = numpy.array((1),dtype=pu.dim_t) 36 | if(s_a.size > 4): 37 | raise NotImplementedError('Only up to 4 dimensions are supported') 38 | if(af_array is not None): 39 | self.d_array = af_array 40 | # Remove leading and trailing dimensions of size 1 41 | af_dims = list(af_array.dims()) 42 | while len(af_dims) and af_dims[-1] == 1: 43 | af_dims.pop() 44 | if s_a.shape == (): 45 | arg_dims = [1] 46 | else: 47 | arg_dims = list(s_a) 48 | while len(arg_dims) and arg_dims[-1] == 1: 49 | arg_dims.pop() 50 | if af_dims != arg_dims: 51 | raise ValueError('shape argument not consistent with the dimensions of the af_array given') 52 | else: 53 | out_arr = ctypes.c_void_p(0) 54 | if(buffer is not None): 55 | if buffer_type == 'python': 56 | # normal python buffer. We copy the data to arrayfire 57 | ptr = numpy.frombuffer(buffer, dtype='int8').ctypes.data 58 | arrayfire.backend.get().af_create_array(ctypes.pointer(out_arr), ctypes.c_void_p(ptr), 59 | s_a.size, ctypes.c_void_p(s_a.ctypes.data), pu.typemap(dtype).value) 60 | elif buffer_type == afnumpy.arrayfire.get_active_backend(): 61 | # in this case buffer is a device memory address. We create the array without copying 62 | ptr = buffer 63 | arrayfire.backend.get().af_device_array(ctypes.pointer(out_arr), ctypes.c_void_p(ptr), 64 | s_a.size, ctypes.c_void_p(s_a.ctypes.data), pu.typemap(dtype).value) 65 | # Do not release the memory on destruction 66 | arrayfire.backend.get().af_retain_array(ctypes.pointer(out_arr),out_arr) 67 | else: 68 | raise ValueError("buffer_type must match afnumpy.arrayfire.get_active_backend() or be 'python'") 69 | else: 70 | arrayfire.backend.get().af_create_handle(ctypes.pointer(out_arr), s_a.size, ctypes.c_void_p(s_a.ctypes.data), pu.typemap(dtype).value) 71 | self.d_array = arrayfire.Array() 72 | self.d_array.arr = out_arr 73 | 74 | # Check if array size matches the af_array size 75 | # This is necessary as certain operations that cause reduction in 76 | # dimensions in numpy do not necessarily do that in arrayfire 77 | if af_array is not None and self.d_array.dims() != pu.c2f(self._shape): 78 | self.__reshape__(self._shape) 79 | 80 | 81 | def __repr__(self): 82 | h_array = numpy.empty(shape=self.shape, dtype=self.dtype) 83 | if self.size: 84 | arrayfire.backend.get().af_get_data_ptr(ctypes.c_void_p(h_array.ctypes.data), self.d_array.arr) 85 | return h_array.__repr__() 86 | 87 | def __str__(self): 88 | return self.__repr__() 89 | 90 | def __format__(self, f): 91 | h_array = numpy.empty(shape=self.shape, dtype=self.dtype) 92 | if self.size: 93 | arrayfire.backend.get().af_get_data_ptr(ctypes.c_void_p(h_array.ctypes.data), self.d_array.arr) 94 | return h_array.__format__(f) 95 | 96 | @ufunc 97 | def __add__(self, other): 98 | s = self.d_array + pu.raw(other) 99 | a = ndarray(self.shape, dtype=pu.typemap(s.dtype()), af_array=s) 100 | a._eval() 101 | return a 102 | 103 | @iufunc 104 | def __iadd__(self, other): 105 | afnumpy.add(self, pu.raw(other), out=self) 106 | self._eval() 107 | return self 108 | 109 | def __radd__(self, other): 110 | s = pu.raw(other) + self.d_array 111 | a = ndarray(self.shape, dtype=pu.typemap(s.dtype()), af_array=s) 112 | a._eval() 113 | return a 114 | 115 | @ufunc 116 | def __sub__(self, other): 117 | s = self.d_array - pu.raw(other) 118 | a = ndarray(self.shape, dtype=pu.typemap(s.dtype()), af_array=s) 119 | a._eval() 120 | return a 121 | 122 | @iufunc 123 | def __isub__(self, other): 124 | afnumpy.subtract(self, pu.raw(other), out=self) 125 | self._eval() 126 | return self 127 | 128 | def __rsub__(self, other): 129 | s = pu.raw(other) - self.d_array 130 | a = ndarray(self.shape, dtype=pu.typemap(s.dtype()), af_array=s) 131 | a._eval() 132 | return a 133 | 134 | @ufunc 135 | def __mul__(self, other): 136 | s = self.d_array * pu.raw(other) 137 | a = ndarray(self.shape, dtype=pu.typemap(s.dtype()), af_array=s) 138 | a._eval() 139 | return a 140 | 141 | @iufunc 142 | def __imul__(self, other): 143 | afnumpy.multiply(self, pu.raw(other), out=self) 144 | self._eval() 145 | return self 146 | 147 | def __rmul__(self, other): 148 | s = pu.raw(other) * self.d_array 149 | a = ndarray(self.shape, dtype=pu.typemap(s.dtype()), af_array=s) 150 | a._eval() 151 | return a 152 | 153 | @ufunc 154 | def __div__(self, other): 155 | s = self.d_array / pu.raw(other) 156 | a = ndarray(self.shape, dtype=pu.typemap(s.dtype()), af_array=s) 157 | a._eval() 158 | return a 159 | 160 | __floordiv__ = __div__ 161 | 162 | @ufunc 163 | def __truediv__(self, other): 164 | # Check if we need to cast input to floating point to get a true division 165 | if(pu.isintegertype(self) and pu.isintegertype(other)): 166 | s = self.astype(numpy.float32).d_array / pu.raw(other) 167 | else: 168 | s = self.d_array / pu.raw(other) 169 | a = ndarray(self.shape, dtype=pu.typemap(s.dtype()), af_array=s) 170 | a._eval() 171 | return a 172 | 173 | @iufunc 174 | def __idiv__(self, other): 175 | afnumpy.floor_divide(self, pu.raw(other), out=self) 176 | self._eval() 177 | return self 178 | 179 | @iufunc 180 | def __itruediv__(self, other): 181 | afnumpy.true_divide(self, pu.raw(other), out=self) 182 | self._eval() 183 | return self 184 | 185 | def __rdiv__(self, other): 186 | s = pu.raw(other) / self.d_array 187 | a = ndarray(self.shape, dtype=pu.typemap(s.dtype()), af_array=s) 188 | a._eval() 189 | return a 190 | 191 | 192 | def __rtruediv__(self, other): 193 | # Check if we need to cast input to floating point to get a true division 194 | if(pu.isintegertype(self) and pu.isintegertype(other)): 195 | s = pu.raw(other) / self.astype(numpy.float32).d_array 196 | else: 197 | s = pu.raw(other) / self.d_array 198 | a = ndarray(self.shape, dtype=pu.typemap(s.dtype()), af_array=s) 199 | a._eval() 200 | return a 201 | 202 | def __pow__(self, other): 203 | if(isinstance(other, numbers.Number) and numpy.issubdtype(type(other), numpy.float64) and 204 | numpy.issubdtype(self.dtype, numpy.integer)): 205 | # AF does not automatically upconvert A**0.5 to float for integer arrays 206 | s = arrayfire.pow(self.astype(type(other)).d_array, pu.raw(other)) 207 | else: 208 | s = arrayfire.pow(self.d_array, pu.raw(other)) 209 | a = ndarray(self.shape, dtype=pu.typemap(s.dtype()), af_array=s) 210 | a._eval() 211 | return a 212 | 213 | def __rpow__(self, other): 214 | if(isinstance(other, numbers.Number) and numpy.issubdtype(type(other), numpy.float64) and 215 | numpy.issubdtype(self.dtype, numpy.integer)): 216 | # AF does not automatically upconvert A**0.5 to float for integer arrays 217 | s = arrayfire.pow(pu.raw(other), self.astype(type(other)).d_array) 218 | else: 219 | s = arrayfire.pow(pu.raw(other), self.d_array) 220 | a = ndarray(self.shape, dtype=pu.typemap(s.dtype()), af_array=s) 221 | a._eval() 222 | return a 223 | 224 | def __lt__(self, other): 225 | s = self.d_array < pu.raw(other) 226 | a = ndarray(self.shape, dtype=numpy.bool, af_array=s) 227 | a._eval() 228 | return a 229 | 230 | def __le__(self, other): 231 | s = self.d_array <= pu.raw(other) 232 | a = ndarray(self.shape, dtype=numpy.bool, af_array=s) 233 | a._eval() 234 | return a 235 | 236 | def __gt__(self, other): 237 | s = self.d_array > pu.raw(other) 238 | a = ndarray(self.shape, dtype=numpy.bool, af_array=s) 239 | a._eval() 240 | return a 241 | 242 | def __ge__(self, other): 243 | s = self.d_array >= pu.raw(other) 244 | a = ndarray(self.shape, dtype=numpy.bool, af_array=s) 245 | a._eval() 246 | return a 247 | 248 | def __eq__(self, other): 249 | if(other is None): 250 | return False 251 | s = self.d_array == pu.raw(other) 252 | a = ndarray(self.shape, dtype=numpy.bool, af_array=s) 253 | a._eval() 254 | return a 255 | 256 | def __ne__(self, other): 257 | if(other is None): 258 | return True 259 | s = self.d_array != pu.raw(other) 260 | a = ndarray(self.shape, dtype=numpy.bool, af_array=s) 261 | a._eval() 262 | return a 263 | 264 | def __abs__(self): 265 | s = arrayfire.abs(self.d_array) 266 | # dtype is wrong for complex types 267 | return ndarray(self.shape, dtype=pu.typemap(s.dtype()), af_array=s) 268 | 269 | def __neg__(self): 270 | if self.dtype == numpy.dtype('bool'): 271 | # Special case for boolean getitem 272 | a = afnumpy.array([True]) - self 273 | else: 274 | a = self * self.dtype.type(-1) 275 | a._eval() 276 | return a 277 | 278 | def __pos__(self): 279 | return afnumpy.array(self) 280 | 281 | def __invert__(self): 282 | raise NotImplementedError 283 | 284 | def __nonzero__(self): 285 | # This should be improved 286 | return numpy.array(self).__nonzero__() 287 | 288 | def __len__(self): 289 | return self.shape[0] 290 | 291 | def __mod__(self, other): 292 | s = self.d_array % pu.raw(other) 293 | a = ndarray(self.shape, dtype=pu.typemap(s.dtype()), af_array=s) 294 | a._eval() 295 | return a 296 | 297 | def __rmod__(self, other): 298 | s = pu.raw(other) % self.d_array 299 | a = ndarray(self.shape, dtype=pu.typemap(s.dtype()), af_array=s) 300 | a._eval() 301 | return a 302 | 303 | @property 304 | def ndim(self): 305 | return len(self.shape) 306 | 307 | @property 308 | def size(self): 309 | return numpy.prod(self.shape) 310 | 311 | @property 312 | def shape(self): 313 | return self._shape 314 | 315 | @shape.setter 316 | def shape(self, value): 317 | self.__reshape__(value) 318 | 319 | @property 320 | def flat(self): 321 | # Currently arrayfire.flat is doing unnecessary copies 322 | # ret = ndarray(self.size, dtype=self.dtype, af_array=arrayfire.flat(self.d_array)) 323 | ret = self.reshape(-1) 324 | ret._base = self 325 | return ret 326 | 327 | @property 328 | def real(self): 329 | ret_type = numpy.real(numpy.zeros((),dtype=self.dtype)).dtype 330 | shape = list(self.shape) 331 | if not numpy.issubdtype(self.dtype, numpy.complexfloating): 332 | return self 333 | 334 | shape[-1] *= 2 335 | dims = numpy.array(pu.c2f(shape),dtype=pu.dim_t) 336 | s = arrayfire.Array() 337 | arrayfire.backend.get().af_device_array(ctypes.pointer(s.arr), 338 | ctypes.c_void_p(self.d_array.device_ptr()), 339 | self.ndim, 340 | ctypes.c_void_p(dims.ctypes.data), 341 | pu.typemap(ret_type).value) 342 | arrayfire.backend.get().af_retain_array(ctypes.pointer(s.arr),s.arr) 343 | a = ndarray(shape, dtype=ret_type, af_array=s) 344 | ret = a[...,::2] 345 | ret._base = a 346 | ret._base_index = (Ellipsis, slice(None,None,2)) 347 | return ret 348 | 349 | @property 350 | def imag(self): 351 | ret_type = numpy.real(numpy.zeros((),dtype=self.dtype)).dtype 352 | shape = list(self.shape) 353 | if not numpy.issubdtype(self.dtype, numpy.complexfloating): 354 | return afnumpy.zeros(self.shape) 355 | shape[-1] *= 2 356 | dims = numpy.array(pu.c2f(shape),dtype=pu.dim_t) 357 | s = arrayfire.Array() 358 | arrayfire.backend.get().af_device_array(ctypes.pointer(s.arr), 359 | ctypes.c_void_p(self.d_array.device_ptr()), 360 | self.ndim, 361 | ctypes.c_void_p(dims.ctypes.data), 362 | pu.typemap(ret_type).value) 363 | arrayfire.backend.get().af_retain_array(ctypes.pointer(s.arr),s.arr) 364 | a = ndarray(shape, dtype=ret_type, af_array=s) 365 | ret = a[...,1::2] 366 | ret._base = a 367 | ret._base_index = (Ellipsis, slice(1,None,2)) 368 | return ret 369 | 370 | def ravel(self, order=None): 371 | if(order != None and order != 'K' and order != 'C'): 372 | raise NotImplementedError('order %s not supported' % (order)) 373 | return self.flat 374 | 375 | def __iter__(self): 376 | ret = [] 377 | for i in range(0,len(self)): 378 | ret.append(self[i]) 379 | return iter(ret) 380 | 381 | def __getitem__(self, args): 382 | if not isinstance(args, tuple): 383 | args = (args,) 384 | try: 385 | idx, new_shape, input_shape = indexing.__convert_dim__(self.shape, args) 386 | except NotImplementedError: 387 | # Slow indexing method for not currently implemented fancy indexing 388 | return afnumpy.array(numpy.array(self).__getitem__(args)) 389 | if numpy.prod(new_shape) == 0: 390 | # We're gonna end up with an empty array 391 | # As we don't yet support empty arrays return an empty numpy array 392 | return numpy.empty(new_shape, dtype=self.dtype) 393 | 394 | if any(x is None for x in idx): 395 | # one of the indices is empty 396 | return ndarray(indexing.__index_shape__(self.shape, idx), dtype=self.dtype) 397 | idx = tuple(idx) 398 | if len(idx) == 0: 399 | idx = tuple([0]) 400 | s = self.reshape(input_shape).d_array[idx] 401 | shape = pu.af_shape(s) 402 | array = ndarray(shape, dtype=self.dtype, af_array=s) 403 | if(shape != new_shape): 404 | array = array.reshape(new_shape) 405 | 406 | if new_shape == () and Ellipsis not in args: 407 | # Return the actual scalar 408 | return numpy.array(array)[()] 409 | 410 | return array 411 | 412 | def __setitem__(self, idx, value): 413 | try: 414 | idx, idx_shape, input_shape = indexing.__convert_dim__(self.shape, idx) 415 | except NotImplementedError: 416 | # Slow indexing method for not currently implemented fancy indexing 417 | a = numpy.array(self) 418 | a.__setitem__(idx, value) 419 | self[...] = afnumpy.array(a) 420 | return 421 | 422 | if numpy.prod(idx_shape) == 0: 423 | # We've selected an empty array 424 | # No need to do anything 425 | return 426 | if any(x is None for x in idx): 427 | # one of the indices is empty 428 | return 429 | idx = tuple(idx) 430 | if len(idx) == 0: 431 | idx = tuple([0]) 432 | if(isinstance(value, numbers.Number)): 433 | pass 434 | elif(isinstance(value, ndarray)): 435 | if(value.dtype != self.dtype): 436 | value.astype(self.dtype) 437 | value = indexing.__expand_dim__(self.shape, value, idx).d_array 438 | else: 439 | raise NotImplementedError('values must be a afnumpy.ndarray') 440 | self.reshape(input_shape).d_array[idx] = value 441 | # This is a hack to be able to make it look like arrays with stride[0] > 1 can still be views 442 | # In practise right now it only applies to ndarray.real and ndarray.imag 443 | try: 444 | self._base[self._base_index] = self 445 | except AttributeError: 446 | pass 447 | 448 | def __array__(self, dtype=None): 449 | h_array = numpy.empty(shape=self.shape,dtype=self.dtype) 450 | if self.size: 451 | arrayfire.backend.get().af_get_data_ptr(ctypes.c_void_p(h_array.ctypes.data), self.d_array.arr) 452 | if dtype is None: 453 | return h_array 454 | else: 455 | return h_array.astype(dtype) 456 | 457 | def transpose(self, *axes): 458 | if(self.ndim == 1): 459 | return self 460 | if len(axes) == 0 and self.ndim == 2: 461 | s = arrayfire.transpose(self.d_array) 462 | else: 463 | order = [0,1,2,3] 464 | if len(axes) == 0 or axes[0] is None: 465 | order[:self.ndim] = order[:self.ndim][::-1] 466 | else: 467 | if isinstance(axes[0], collections.Iterable): 468 | axes = axes[0] 469 | for i,ax in enumerate(axes): 470 | order[i] = pu.c2f(self.shape, ax) 471 | # We have to do this gymnastic due to the fact that arrayfire 472 | # uses Fortran order 473 | order[:len(axes)] = order[:len(axes)][::-1] 474 | 475 | #print order 476 | s = arrayfire.reorder(self.d_array, order[0],order[1],order[2],order[3]) 477 | return ndarray(pu.af_shape(s), dtype=self.dtype, af_array=s) 478 | 479 | def reshape(self, shape, order = 'C'): 480 | a = afnumpy.array(self, copy=False) 481 | a.__reshape__(shape, order) 482 | return a 483 | 484 | # In place reshape 485 | def __reshape__(self, newshape, order = 'C'): 486 | if(order != 'C'): 487 | raise NotImplementedError 488 | if isinstance(newshape,numbers.Number): 489 | newshape = (newshape,) 490 | # Replace a possible -1 with the 491 | if -1 in newshape: 492 | newshape = list(newshape) 493 | i = newshape.index(-1) 494 | newshape[i] = 1 495 | if -1 in newshape: 496 | raise ValueError('Only one -1 allowed in shape') 497 | newshape[i] = self.size//numpy.prod(newshape) 498 | if self.size != numpy.prod(newshape): 499 | raise ValueError('total size of new array must be unchanged') 500 | if len(newshape) != 0: 501 | # No need to modify the af_array for empty shapes 502 | # af_shape = numpy.array(pu.c2f(newshape), dtype=pu.dim_t) 503 | # s = arrayfire.Array() 504 | # arrayfire.backend.get().af_moddims(ctypes.pointer(s.arr), self.d_array.arr, af_shape.size, ctypes.c_void_p(af_shape.ctypes.data)) 505 | # self.d_array = s 506 | if tuple(newshape) == self.shape: 507 | # No need to do anything 508 | return 509 | af_shape = numpy.array(pu.c2f(newshape), dtype=pu.dim_t) 510 | s = arrayfire.Array() 511 | arrayfire.backend.get().af_moddims(ctypes.pointer(s.arr), self.d_array.arr, 512 | af_shape.size, ctypes.c_void_p(af_shape.ctypes.data)) 513 | self.d_array = s 514 | 515 | self._shape = tuple(newshape) 516 | 517 | def flatten(self, order='C'): 518 | if(order != None and order != 'K' and order != 'C' and order != 'A'): 519 | raise NotImplementedError('order %s not supported' % (order)) 520 | return afnumpy.reshape(self, self.size).copy() 521 | 522 | @reductufunc 523 | def max(self, s, axis): 524 | return arrayfire.max(s, axis) 525 | 526 | @reductufunc 527 | def min(self, s, axis): 528 | return arrayfire.min(s, axis) 529 | 530 | def astype(self, dtype, order='K', casting='unsafe', subok=True, copy=True): 531 | if(order != 'K'): 532 | raise NotImplementedError('only order=K implemented') 533 | if(casting != 'unsafe'): 534 | raise NotImplementedError('only casting=unsafe implemented') 535 | if(copy == False and order == 'K' and dtype == self.dtype): 536 | return self 537 | s = arrayfire.cast(self.d_array, pu.typemap(dtype)) 538 | a = ndarray(self.shape, dtype=dtype, af_array=s) 539 | a._eval() 540 | return a 541 | 542 | 543 | def round(self, decimals=0, out=None): 544 | if decimals != 0: 545 | raise NotImplementedError('only supports decimals=0') 546 | s = arrayfire.round(self.d_array) 547 | ret = ndarray(self.shape, dtype=pu.typemap(s.dtype()), af_array=s) 548 | if(out): 549 | out[:] = ret[:] 550 | return ret 551 | 552 | 553 | def take(self, indices, axis=None, out=None, mode='raise'): 554 | if mode != 'raise': 555 | raise NotImplementedError('only supports mode=raise') 556 | if axis is None: 557 | ret = self.flat[indices] 558 | else: 559 | ret = self[(slice(None),)*axis+(indices,)] 560 | if out: 561 | out[:] = ret[:] 562 | return ret 563 | 564 | @outufunc 565 | @reductufunc 566 | def sum(self, s, axis): 567 | if self.dtype == numpy.bool: 568 | s = arrayfire.cast(s, pu.typemap(numpy.int64)) 569 | # s = s.astype(pu.typemap(numpy.int64)) 570 | return arrayfire.sum(s, dim=axis) 571 | 572 | @outufunc 573 | @reductufunc 574 | def mean(self, s, axis): 575 | if self.dtype == numpy.bool: 576 | s = s.astype(pu.typemap(numpy.float64)) 577 | return arrayfire.mean(s, dim=axis) 578 | 579 | @outufunc 580 | @reductufunc 581 | def prod(self, s, axis): 582 | if self.dtype == numpy.bool: 583 | s = s.astype(pu.typemap(numpy.int64)) 584 | return arrayfire.product(s, dim=axis) 585 | 586 | product = prod 587 | 588 | @outufunc 589 | @reductufunc 590 | def all(self, s, axis): 591 | return arrayfire.all_true(s, dim=axis) 592 | 593 | @outufunc 594 | @reductufunc 595 | def any(self, s, axis): 596 | return arrayfire.any_true(s, dim=axis) 597 | 598 | 599 | def conj(self): 600 | if not numpy.issubdtype(self.dtype, numpy.complex): 601 | return afnumpy.copy(self) 602 | s = arrayfire.conjg(self.d_array) 603 | return ndarray(self.shape, dtype=pu.typemap(s.dtype()), af_array=s) 604 | 605 | # Convert to float 606 | def __float__(self): 607 | if self.size != 1: 608 | raise TypeError('only length-1 arrays can be converted to Python scalars') 609 | ret = self[(0,)*self.ndim] 610 | return ret 611 | 612 | def squeeze(self, axis=None): 613 | if axis is None: 614 | axis = tuple(i for i, x in enumerate(self.shape) if x == 1) 615 | if not isinstance(axis, tuple): 616 | axis = (axis,) 617 | newshape = list(self.shape) 618 | for a in sorted(axis)[::-1]: 619 | newshape.pop(a) 620 | return self.reshape(newshape) 621 | 622 | @property 623 | def T(self): 624 | if self.ndim < 2: 625 | return self 626 | return self.transpose() 627 | 628 | def argmax(self, axis=None): 629 | if axis is None: 630 | return self.flat.argmax(axis=0) 631 | if not isinstance(axis, numbers.Number): 632 | raise TypeError('an integer is required for the axis') 633 | val, idx = arrayfire.imax(self.d_array, pu.c2f(self.shape, axis)) 634 | shape = list(self.shape) 635 | shape.pop(axis) 636 | if(len(shape)): 637 | return ndarray(shape, dtype=pu.typemap(idx.dtype()), af_array=idx) 638 | else: 639 | return ndarray(shape, dtype=pu.typemap(idx.dtype()), af_array=idx)[()] 640 | 641 | def argmin(self, axis=None): 642 | if axis is None: 643 | return self.flat.argmin(axis=0) 644 | if not isinstance(axis, numbers.Number): 645 | raise TypeError('an integer is required for the axis') 646 | val, idx = arrayfire.imin(self.d_array, pu.c2f(self.shape, axis)) 647 | shape = list(self.shape) 648 | shape.pop(axis) 649 | if(len(shape)): 650 | return ndarray(shape, dtype=pu.typemap(idx.dtype()), af_array=idx) 651 | else: 652 | return ndarray(shape, dtype=pu.typemap(idx.dtype()), af_array=idx)[()] 653 | 654 | 655 | def argsort(self, axis=-1, kind='quicksort', order=None): 656 | if kind != 'quicksort': 657 | print( "argsort 'kind' argument ignored" ) 658 | if order is not None: 659 | raise ValueError('order argument is not supported') 660 | if(axis is None): 661 | input = self.flatten() 662 | axis = 0 663 | else: 664 | input = self 665 | if(axis < 0): 666 | axis = self.ndim+axis 667 | val, idx = arrayfire.sort_index(input.d_array, pu.c2f(input.shape, axis)) 668 | return ndarray(input.shape, dtype=pu.typemap(idx.dtype()), af_array=idx) 669 | 670 | @property 671 | def base(self): 672 | return self._base 673 | 674 | @property 675 | def strides(self): 676 | strides = () 677 | # we have access to the stride functions 678 | if afnumpy.arrayfire_version(numeric=True) >= 3003000: 679 | strides = pu.c2f(self.d_array.strides()[0:self.ndim]) 680 | if len(strides) < self.ndim and self.ndim > 1: 681 | strides = (strides[0]*self.shape[1],) + strides 682 | strides = tuple([s*self.dtype.itemsize for s in strides]) 683 | else: 684 | idx = (slice(1,None),) 685 | base_addr = self.d_array.device_ptr() 686 | dims = self.d_array.dims() 687 | # Append any missing ones 688 | dims = dims + (1,)*(self.ndim-len(dims)) 689 | for i in range(0, self.ndim): 690 | if(dims[i] > 1): 691 | strides = (self.d_array[idx].device_ptr()-base_addr,)+strides 692 | else: 693 | if len(strides): 694 | strides = (dims[i-1]*numpy.prod(strides),)+strides 695 | else: 696 | strides = (self.itemsize,)+strides 697 | idx = (slice(None),)+idx 698 | return strides 699 | 700 | @property 701 | def itemsize(self): 702 | return self.dtype.itemsize 703 | 704 | def eval(self): 705 | return arrayfire.backend.get().af_eval(self.d_array.arr) 706 | 707 | def _eval(self): 708 | if afnumpy.force_eval: 709 | return self.eval() 710 | else: 711 | return 0 712 | 713 | def copy(self, order='C'): 714 | return afnumpy.array(self, copy=True, order=order) 715 | 716 | def nonzero(self): 717 | s = arrayfire.where(self.d_array) 718 | s = ndarray(pu.af_shape(s), dtype=numpy.uint32, 719 | af_array=s).astype(numpy.int64) 720 | # TODO: Unexplained eval 721 | s.eval() 722 | idx = [] 723 | mult = 1 724 | for i in self.shape[::-1]: 725 | mult = i 726 | idx = [s % mult] + idx 727 | s //= mult 728 | idx = tuple(idx) 729 | return idx 730 | 731 | def sort(self, axis=-1, kind='quicksort', order=None): 732 | if kind != 'quicksort': 733 | print( "sort 'kind' argument ignored" ) 734 | if order is not None: 735 | raise ValueError('order argument is not supported') 736 | if(axis is None): 737 | input = self.flatten() 738 | axis = 0 739 | else: 740 | input = self 741 | if(axis < 0): 742 | axis = self.ndim+axis 743 | s = arrayfire.sort(input.d_array, pu.c2f(input.shape, axis)) 744 | self.d_array = s 745 | 746 | def repeat(self, repeats, axis=None): 747 | # Cop out, as repeat is usually not that performance sensitive 748 | return afnumpy.array(numpy.array(self).repeat(repeats,axis)) 749 | --------------------------------------------------------------------------------