Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
75c1d43
Refactoring of basic functionality to create an empty Array
Jan 24, 2023
b14aa91
Replace dim4 with CShape
roaffix Jan 24, 2023
eadbe9b
Add tests. Minor fixes. Update CI
roaffix Jan 24, 2023
c13a59f
Fix CI
roaffix Jan 24, 2023
f0f57e8
Add arithmetic operators w/o tests
roaffix Jan 26, 2023
8cef774
Fix array init bug. Add __getitem__. Change pytest for active debug mode
roaffix Jan 27, 2023
a4c7ac9
Add reflected arithmetic and array operators
roaffix Jan 27, 2023
4140527
Place TODO for repr
roaffix Jan 28, 2023
4374d93
Add bitwise operators. Add in-place operators. Add missing reflected …
roaffix Jan 28, 2023
5a29ffa
Fix tests
roaffix Jan 28, 2023
4187b27
Add tests for arithmetic operators
roaffix Jan 28, 2023
cdb7a92
Added to_list and to_ctypes_array
roaffix Jan 28, 2023
9c0435a
Fix bug when scalar is empty returns None
roaffix Jan 28, 2023
769c16c
Fix typing in array object. Add tests
roaffix Jan 29, 2023
fb27e46
Change tests and found bug with reflected operators
roaffix Jan 29, 2023
0afb92e
Fix reflected operators bug. Add test coverage for the rest of the ar…
roaffix Jan 29, 2023
1d071be
Add required by specification methods
roaffix Jan 30, 2023
04fbb1b
Change file names
roaffix Jan 30, 2023
2d91b04
Change utils. Add docstrings
roaffix Jan 30, 2023
5939388
Add docstrings for operators
roaffix Jan 30, 2023
0231e27
Change TODOs
roaffix Jan 30, 2023
07c4206
Add docstrings for other operators. Remove docstrings from mocks
roaffix Jan 30, 2023
908447b
Change tags and typings
roaffix Feb 4, 2023
fa3ad06
Change typings from python 3.10 to python 3.8
roaffix Feb 4, 2023
0de9955
Add readme with reference to run tests
roaffix Feb 4, 2023
ae6be05
Revert changes accidentally made in original array
roaffix Feb 5, 2023
cfa9114
Add initial refactoring with backend mock
roaffix Feb 8, 2023
5de8694
Add c library methods for operators
roaffix Feb 9, 2023
b9ac1c5
Remove dependency on default backend
roaffix Feb 10, 2023
171ec88
Refactor backend and project structure
roaffix Feb 10, 2023
e984caa
Refactor backend library operators
roaffix Feb 11, 2023
0b164d4
Refactor used in array_object backend methods
roaffix Feb 11, 2023
282f860
Minor test fix
roaffix Feb 11, 2023
54f7ada
Refactor tests
roaffix Feb 13, 2023
51f6efd
Add comparison operators tests
roaffix Feb 13, 2023
23e2635
Minor fixes for tests
roaffix Feb 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Replace dim4 with CShape
  • Loading branch information
roaffix committed Jan 24, 2023
commit b14aa9124afb0978c561ea78db9da9dc1546c5ab
3 changes: 1 addition & 2 deletions arrayfire/array_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@
"complex64", "complex128", "bool"]

from ._array_object import Array
from ._dtypes import (
bool, complex64, complex128, float32, float64, int16, int32, int64, uint8, uint16, uint32, uint64)
from ._dtypes import bool, complex64, complex128, float32, float64, int16, int32, int64, uint8, uint16, uint32, uint64
93 changes: 25 additions & 68 deletions arrayfire/array_api/_array_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@

import array as py_array
import ctypes
import math
from dataclasses import dataclass

from arrayfire import backend, safe_call # TODO refactoring
from arrayfire.array import _in_display_dims_limit # TODO refactoring

from ._dtypes import Dtype, c_dim_t, float32, supported_dtypes
from ._dtypes import CShape, Dtype, c_dim_t, float32, supported_dtypes
from ._utils import Device, PointerSource, to_str

ShapeType = tuple[None | int, ...]
Expand All @@ -28,7 +27,6 @@ class Array:
__array_priority__ = 30

# Initialisation
_array_buffer = _ArrayBuffer()
arr = ctypes.c_void_p(0)

def __init__(
Expand All @@ -46,12 +44,12 @@ def __init__(
if x is None:
if not shape: # shape is None or empty tuple
safe_call(backend.get().af_create_handle(
ctypes.pointer(self.arr), 0, ctypes.pointer(dim4()), dtype.c_api_value))
ctypes.pointer(self.arr), 0, ctypes.pointer(CShape().c_array), dtype.c_api_value))
return

# NOTE: applies inplace changes for self.arr
safe_call(backend.get().af_create_handle(
ctypes.pointer(self.arr), len(shape), ctypes.pointer(dim4(*shape)), dtype.c_api_value))
ctypes.pointer(self.arr), len(shape), ctypes.pointer(CShape(*shape).c_array), dtype.c_api_value))
return

if isinstance(x, Array):
Expand All @@ -61,19 +59,16 @@ def __init__(
if isinstance(x, py_array.array):
_type_char = x.typecode
_array_buffer = _ArrayBuffer(*x.buffer_info())
numdims, idims = _get_info(shape, _array_buffer.length)

elif isinstance(x, list):
_array = py_array.array("f", x) # BUG [True, False] -> dtype: f32 # TODO add int and float
_type_char = _array.typecode
_array_buffer = _ArrayBuffer(*_array.buffer_info())
numdims, idims = _get_info(shape, _array_buffer.length)

elif isinstance(x, int) or isinstance(x, ctypes.c_void_p): # TODO
_array_buffer = _ArrayBuffer(x if not isinstance(x, ctypes.c_void_p) else x.value)
numdims, idims = _get_info(shape, _array_buffer.length)

if not math.prod(idims):
if not shape:
raise RuntimeError("Expected to receive the initial shape due to the x being a data pointer.")

if _no_initial_dtype:
Expand All @@ -84,34 +79,37 @@ def __init__(
else:
raise TypeError("Passed object x is an object of unsupported class.")

_cshape = _get_cshape(shape, _array_buffer.length)

if not _no_initial_dtype and dtype.typecode != _type_char:
raise TypeError("Can not create array of requested type from input data type")

if not (offset or strides):
if pointer_source == PointerSource.host:
safe_call(backend.get().af_create_array(
ctypes.pointer(self.arr), ctypes.c_void_p(_array_buffer.address), numdims,
ctypes.pointer(dim4(*idims)), dtype.c_api_value))
ctypes.pointer(self.arr), ctypes.c_void_p(_array_buffer.address), _cshape.original_shape,
ctypes.pointer(_cshape.c_array), dtype.c_api_value))
return

safe_call(backend.get().af_device_array(
ctypes.pointer(self.arr), ctypes.c_void_p(_array_buffer.address), numdims,
ctypes.pointer(dim4(*idims)), dtype.c_api_value))
ctypes.pointer(self.arr), ctypes.c_void_p(_array_buffer.address), _cshape.original_shape,
ctypes.pointer(_cshape.c_array), dtype.c_api_value))
return

if offset is None: # TODO
if offset is None:
offset = c_dim_t(0)

if strides is None: # TODO
strides = (1, idims[0], idims[0]*idims[1], idims[0]*idims[1]*idims[2])
if strides is None:
strides = (1, _cshape[0], _cshape[0]*_cshape[1], _cshape[0]*_cshape[1]*_cshape[2])

if len(strides) < 4:
strides += (strides[-1], ) * (4 - len(strides))
strides_dim4 = dim4(*strides)
strides_cshape = CShape(*strides).c_array

safe_call(backend.get().af_create_strided_array(
ctypes.pointer(self.arr), ctypes.c_void_p(_array_buffer.address), offset, numdims,
ctypes.pointer(dim4(*idims)), ctypes.pointer(strides_dim4), dtype.c_api_value, pointer_source.value))
ctypes.pointer(self.arr), ctypes.c_void_p(_array_buffer.address), offset, _cshape.original_shape,
ctypes.pointer(_cshape.c_array), ctypes.pointer(strides_cshape), dtype.c_api_value,
pointer_source.value))

def __str__(self) -> str: # FIXME
if not _in_display_dims_limit(self.shape):
Expand All @@ -126,7 +124,7 @@ def __len__(self) -> int:
return self.shape[0] if self.shape else 0 # type: ignore[return-value]

def __pos__(self) -> Array:
"""y
"""
Return +self
"""
return self
Expand Down Expand Up @@ -190,8 +188,7 @@ def shape(self) -> ShapeType:
d3 = c_dim_t(0)
safe_call(backend.get().af_get_dims(
ctypes.pointer(d0), ctypes.pointer(d1), ctypes.pointer(d2), ctypes.pointer(d3), self.arr))
dims = (d0.value, d1.value, d2.value, d3.value)
return dims[:self.ndim] # FIXME An array dimension must be None if and only if a dimension is unknown
return (d0.value, d1.value, d2.value, d3.value)[:self.ndim] # Skip passing None values

def _as_str(self) -> str:
arr_str = ctypes.c_char_p(0)
Expand All @@ -201,30 +198,6 @@ def _as_str(self) -> str:
safe_call(backend.get().af_free_host(arr_str))
return py_str

# def _get_metadata_str(self, show_dims: bool = True) -> str:
# return (
# "arrayfire.Array()\n"
# f"Type: {self.dtype.typename}\n"
# f"Dims: {str(self._dims) if show_dims else ''}")

# @property
# def dtype(self) -> ...:
# dty = ctypes.c_int()
# safe_call(backend.get().af_get_type(ctypes.pointer(dty), self.arr)) # -> new dty

# @safe_call
# def backend()
# ...

# @backend(safe=True)
# def af_get_type(arr) -> ...:
# dty = ctypes.c_int()
# safe_call(backend.get().af_get_type(ctypes.pointer(dty), self.arr)) # -> new dty
# return dty

# def new_dtype():
# return af_get_type(self.arr)


def _metadata_string(dtype: Dtype, dims: None | ShapeType = None) -> str:
return (
Expand All @@ -233,20 +206,14 @@ def _metadata_string(dtype: Dtype, dims: None | ShapeType = None) -> str:
f"Dims: {str(dims) if dims else ''}")


def _get_info(shape: None | tuple[int], buffer_length: int) -> tuple[int, list[int]]:
# TODO refactor
def _get_cshape(shape: None | tuple[int], buffer_length: int) -> CShape:
if shape:
numdims = len(shape)
idims = [1]*4
for i in range(numdims):
idims[i] = shape[i]
elif (buffer_length != 0):
idims = [buffer_length, 1, 1, 1]
numdims = 1
else:
raise RuntimeError("Invalid size")
return CShape(*shape)

if buffer_length != 0:
return CShape(buffer_length)

return numdims, idims
raise RuntimeError("Shape and buffer length are size invalid.")


def _c_api_value_to_dtype(value: int) -> Dtype:
Expand Down Expand Up @@ -282,16 +249,6 @@ def _str_to_dtype(value: int) -> Dtype:
# return out


def dim4(d0: int = 1, d1: int = 1, d2: int = 1, d3: int = 1): # type: ignore # FIXME
c_dim4 = c_dim_t * 4 # ctypes.c_int | ctypes.c_longlong * 4
out = c_dim4(1, 1, 1, 1)

for i, dim in enumerate((d0, d1, d2, d3)):
if dim is not None:
out[i] = c_dim_t(dim)

return out

# TODO replace candidate below
# def dim4_to_tuple(shape: ShapeType, default: int=1) -> ShapeType:
# assert(isinstance(dims, tuple))
Expand Down
37 changes: 36 additions & 1 deletion arrayfire/array_api/_dtypes.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import ctypes
from dataclasses import dataclass
from typing import Type
Expand Down Expand Up @@ -31,6 +33,39 @@ class Dtype:
bool = Dtype("b", ctypes.c_bool, "bool", 4)

supported_dtypes = [
# int8,
int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, complex64, complex128, bool
]


class CShape(tuple):
def __new__(cls, *args: int) -> CShape:
cls.original_shape = len(args)
return tuple.__new__(cls, args)

def __init__(self, x1: int = 1, x2: int = 1, x3: int = 1, x4: int = 1) -> None:
self.x1 = x1
self.x2 = x2
self.x3 = x3
self.x4 = x4

def __repr__(self) -> str:
return f"{self.__class__.__name__}{self.x1, self.x2, self.x3, self.x4}"

@property
def c_array(self): # type: ignore[no-untyped-def]
c_shape = c_dim_t * 4 # ctypes.c_int | ctypes.c_longlong * 4
return c_shape(c_dim_t(self.x1), c_dim_t(self.x2), c_dim_t(self.x3), c_dim_t(self.x4))


# @safe_call
# def backend()
# ...

# @backend(safe=True)
# def af_get_type(arr) -> ...:
# dty = ctypes.c_int()
# safe_call(backend.get().af_get_type(ctypes.pointer(dty), self.arr)) # -> new dty
# return dty

# def new_dtype():
# return af_get_type(self.arr)
4 changes: 4 additions & 0 deletions arrayfire/array_api/pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[pytest]
addopts = --cache-clear --cov=./arrayfire/array_api --flake8 --mypy --isort ./arrayfire/array_api
console_output_style = classic
markers = mypy
17 changes: 17 additions & 0 deletions arrayfire/array_api/tests/test_array.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import pytest

from arrayfire.array_api import Array, float32


Expand All @@ -9,3 +11,18 @@ def test_empty_array() -> None:
assert array.size == 0
assert array.shape == ()
assert len(array) == 0


def test_array_from_1d_list() -> None:
array = Array([1, 2, 3])

assert array.dtype == float32
assert array.ndim == 1
assert array.size == 3
assert array.shape == (3,)
assert len(array) == 3


def test_array_from_2d_list() -> None:
with pytest.raises(TypeError):
Array([[1, 2, 3], [1, 2, 3]])