Skip to content

Commit

Permalink
ufuncs + tests: added np equivalent copyto (#264)
Browse files Browse the repository at this point in the history
  • Loading branch information
HannanNaeem authored Mar 13, 2024
1 parent 74ad693 commit f02060d
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 0 deletions.
1 change: 1 addition & 0 deletions pykokkos/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
sqrt,
sign,
add,
copyto,
subtract,
dot,
multiply,
Expand Down
50 changes: 50 additions & 0 deletions pykokkos/lib/ufuncs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1320,6 +1320,56 @@ def subtract(viewA, valB):

return out

@pk.workunit
def copyto_impl_2d(tid, viewA, viewB):
r_idx : int = tid / viewA.extent(1)
c_idx : int = tid - r_idx * viewA.extent(1)

viewA[r_idx][c_idx] = viewB[r_idx][c_idx]

@pk.workunit
def copyto_impl_1d(tid, viewA, viewB):
viewA[tid] = viewB[tid]

def copyto(viewA, viewB):
'''
copies values of viewB into valueA for corresponding indicies
Parameters
----------
viewA : pykokkos view
Input view.
valB : pykokkos view or scalar
Input view
Returns
-------
Void
'''

if not isinstance(viewA, ViewType):
raise ValueError("copyto: Cannot copy to a non-view type")
if not isinstance(viewB, ViewType):
raise ValueError("copyto: Cannot copy from a non-view type")
if viewA.shape != viewB.shape:
if not check_broadcastable_impl(viewA, viewB): # if shape is not same check compatibility
raise ValueError("copyto: Views must be broadcastable or of the same size. {} against {}".format(viewA.shape, viewB.shape))
# check if size is same otherwise broadcast and fix
viewA = broadcast_view(viewB, viewA)

# implementation constraint, for now
if viewA.rank() > 2:
raise NotImplementedError("copyto: This version of Pykokkos only supports copyto upto 2D views")

if viewA.rank() == 1:
pk.parallel_for(viewA.shape[0], copyto_impl_1d, viewA=viewA, viewB=viewB)

else:
outRows = viewA.shape[0]
outCols = viewA.shape[1]
totalThreads = outRows * outCols
pk.parallel_for(totalThreads, copyto_impl_2d, viewA=viewA, viewB=viewB)

@pk.workunit
def np_matmul_impl_2d_2d(tid, cols, vec_length, viewA, viewB, viewOut):
r_idx : int = tid / cols
Expand Down
64 changes: 64 additions & 0 deletions tests/test_ufuncs.py
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,70 @@ def test_sign_1d_special_cases(in_arr, pk_dtype, numpy_dtype):
assert_allclose(actual, expected)


@pytest.mark.parametrize("pk_ufunc, numpy_ufunc", [
(pk.copyto, np.copyto),
])
@pytest.mark.parametrize("numpy_dtype", [
(np.float64),
(np.float32),
])
def test_copyto_1d(pk_ufunc, numpy_ufunc, numpy_dtype):
N = 4
M = 7
rng = default_rng(123)
np1 = rng.random((N, M)).astype(numpy_dtype)
np2 = rng.random((N, M)).astype(numpy_dtype)
numpy_ufunc(np1, np2)

view1 = pk.array(np1)
view2 = pk.array(np2)
pk_ufunc(view1, view2)

assert_allclose(np1, view1)


@pytest.mark.parametrize("pk_ufunc, numpy_ufunc", [
(pk.subtract, np.subtract),
])
@pytest.mark.parametrize("numpy_dtype", [
(np.float64),
(np.float32),
])
@pytest.mark.parametrize("test_dim", [
[4,3,4,3], [4,3,1,1], [4,3,1,3], [4,3,4,1], [4,3,1], [4,3,3], [4,3], [4]
])
def test_copyto_broadcast_2d(pk_ufunc, numpy_ufunc, numpy_dtype, test_dim):
np1 = None
np2 = None
rng = default_rng(123)
scalar = 3.0

if len(test_dim) == 4:
np1 = rng.random((test_dim[0], test_dim[1])).astype(numpy_dtype)
np2 = rng.random((test_dim[2], test_dim[3])).astype(numpy_dtype)
elif len(test_dim) == 3:
np1 = rng.random((test_dim[0], test_dim[1])).astype(numpy_dtype)
np2 = rng.random((test_dim[2])).astype(numpy_dtype)
elif len(test_dim) == 2:
np1 = rng.random((test_dim[0], test_dim[1])).astype(numpy_dtype)
np2 = scalar # 2d with scalar
elif len(test_dim) == 1:
np1 = rng.random((test_dim[0])).astype(numpy_dtype)
np2 = scalar # 1d with scalar
else:
raise NotImplementedError("Invalid test conditions: Broadcasting operations are only supported uptil 2D")

assert np1 is not None and np2 is not None, "Invalid test conditions: Are parameters uptil 2D?"

numpy_ufunc(np1, np2)

view1 = pk.array(np1)
view2 = pk.array(np2) if isinstance(np2, np.ndarray) else np2
pk_ufunc(view1, view2)

assert_allclose(np1, view1)


@pytest.mark.parametrize("input_dtype", [
pk.double, pk.float,
])
Expand Down

0 comments on commit f02060d

Please sign in to comment.