Skip to content

[Data API] Array Object Implementation #261

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Apr 25, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 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
15bc8cb
Add constructor initialisation warning. Add Note on deviation from sp…
roaffix Apr 25, 2023
e24e478
Fix warning message
roaffix Apr 25, 2023
f3a80c0
Add NOTE tag to functions that are not a part of spec but custom solu…
roaffix Apr 25, 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
Add docstrings for other operators. Remove docstrings from mocks
  • Loading branch information
roaffix committed Jan 30, 2023
commit 07c42060aeba5fc0a7bb60e6ea0d53665ede92c8
170 changes: 154 additions & 16 deletions arrayfire/array_api/array_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ def __and__(self, other: int | bool | Array, /) -> Array:
----------
self : Array
Array instance. Should have a numeric data type.
other: int | float | Array
other: int | bool | Array
Other array. Must be compatible with self (see Broadcasting). Should have a numeric data type.

Returns
Expand All @@ -354,7 +354,7 @@ def __or__(self, other: int | bool | Array, /) -> Array:
----------
self : Array
Array instance. Should have a numeric data type.
other: int | float | Array
other: int | bool | Array
Other array. Must be compatible with self (see Broadcasting). Should have a numeric data type.

Returns
Expand All @@ -374,7 +374,7 @@ def __xor__(self, other: int | bool | Array, /) -> Array:
----------
self : Array
Array instance. Should have a numeric data type.
other: int | float | Array
other: int | bool | Array
Other array. Must be compatible with self (see Broadcasting). Should have a numeric data type.

Returns
Expand All @@ -394,7 +394,7 @@ def __lshift__(self, other: int | Array, /) -> Array:
----------
self : Array
Array instance. Should have a numeric data type.
other: int | float | Array
other: int | Array
Other array. Must be compatible with self (see Broadcasting). Should have a numeric data type.
Each element must be greater than or equal to 0.

Expand All @@ -414,7 +414,7 @@ def __rshift__(self, other: int | Array, /) -> Array:
----------
self : Array
Array instance. Should have a numeric data type.
other: int | float | Array
other: int | Array
Other array. Must be compatible with self (see Broadcasting). Should have a numeric data type.
Each element must be greater than or equal to 0.

Expand All @@ -429,44 +429,121 @@ def __rshift__(self, other: int | Array, /) -> Array:

def __lt__(self, other: int | float | Array, /) -> Array:
"""
Return self < other.
Computes the truth value of self_i < other_i for each element of an array instance with the respective
element of the array other.

Parameters
----------
self : Array
Array instance. Should have a numeric data type.
other: int | float | Array
Other array. Must be compatible with self (see Broadcasting). Should have a real-valued data type.

Returns
-------
out : Array
An array containing the element-wise results. The returned array must have a data type of bool.
"""
return _process_c_function(self, other, backend.get().af_lt)

def __le__(self, other: int | float | Array, /) -> Array:
"""
Return self <= other.
Computes the truth value of self_i <= other_i for each element of an array instance with the respective
element of the array other.

Parameters
----------
self : Array
Array instance. Should have a numeric data type.
other: int | float | Array
Other array. Must be compatible with self (see Broadcasting). Should have a real-valued data type.

Returns
-------
out : Array
An array containing the element-wise results. The returned array must have a data type of bool.
"""
return _process_c_function(self, other, backend.get().af_le)

def __gt__(self, other: int | float | Array, /) -> Array:
"""
Return self > other.
Computes the truth value of self_i > other_i for each element of an array instance with the respective
element of the array other.

Parameters
----------
self : Array
Array instance. Should have a numeric data type.
other: int | float | Array
Other array. Must be compatible with self (see Broadcasting). Should have a real-valued data type.

Returns
-------
out : Array
An array containing the element-wise results. The returned array must have a data type of bool.
"""
return _process_c_function(self, other, backend.get().af_gt)

def __ge__(self, other: int | float | Array, /) -> Array:
"""
Return self >= other.
Computes the truth value of self_i >= other_i for each element of an array instance with the respective
element of the array other.

Parameters
----------
self : Array
Array instance. Should have a numeric data type.
other: int | float | Array
Other array. Must be compatible with self (see Broadcasting). Should have a real-valued data type.

Returns
-------
out : Array
An array containing the element-wise results. The returned array must have a data type of bool.
"""
return _process_c_function(self, other, backend.get().af_ge)

def __eq__(self, other: int | float | bool | Array, /) -> Array: # type: ignore[override] # FIXME
"""
Return self == other.
Computes the truth value of self_i == other_i for each element of an array instance with the respective
element of the array other.

Parameters
----------
self : Array
Array instance. Should have a numeric data type.
other: int | float | bool | Array
Other array. Must be compatible with self (see Broadcasting). May have any data type.

Returns
-------
out : Array
An array containing the element-wise results. The returned array must have a data type of bool.
"""
return _process_c_function(self, other, backend.get().af_eq)

def __ne__(self, other: int | float | bool | Array, /) -> Array: # type: ignore[override] # FIXME
"""
Return self != other.
Computes the truth value of self_i != other_i for each element of an array instance with the respective
element of the array other.

Parameters
----------
self : Array
Array instance. Should have a numeric data type.
other: int | float | bool | Array
Other array. Must be compatible with self (see Broadcasting). May have any data type.

Returns
-------
out : Array
An array containing the element-wise results. The returned array must have a data type of bool.
"""
return _process_c_function(self, other, backend.get().af_neq)

# Reflected Arithmetic Operators

def __radd__(self, other: Array, /) -> Array:
# TODO discuss either we need to support complex and bool as other input type
"""
Return other + self.
"""
Expand Down Expand Up @@ -656,8 +733,24 @@ def __float__(self) -> float:
return NotImplemented

def __getitem__(self, key: int | slice | tuple[int | slice] | Array, /) -> Array:
# TODO: API Specification - key: int | slice | ellipsis | tuple[int | slice] | Array - consider using af.span
# TODO: refactor
"""
Returns self[key].

Parameters
----------
self : Array
Array instance.
key : int | slice | tuple[int | slice] | Array
Index key.

Returns
-------
out : Array
An array containing the accessed value(s). The returned array must have the same data type as self.
"""
# TODO
# API Specification - key: int | slice | ellipsis | tuple[int | slice] | Array.
# consider using af.span to replace ellipsis during refactoring
out = Array()
ndims = self.ndim

Expand Down Expand Up @@ -706,6 +799,14 @@ def to_device(self, device: Any, /, *, stream: None | int | Any = None) -> Array

@property
def dtype(self) -> Dtype:
"""
Data type of the array elements.

Returns
-------
out : Dtype
Array data type.
"""
out = ctypes.c_int()
safe_call(backend.get().af_get_type(ctypes.pointer(out), self.arr))
return _c_api_value_to_dtype(out.value)
Expand All @@ -724,29 +825,66 @@ def mT(self) -> Array:
def T(self) -> Array:
"""
Transpose of the array.

Returns
-------
out : Array
Two-dimensional array whose first and last dimensions (axes) are permuted in reverse order relative to
original array. The returned array must have the same data type as the original array.

Note
----
- The array instance must be two-dimensional. If the array instance is not two-dimensional, an error
should be raised.
"""
if self.ndim < 2:
raise TypeError(f"Array should be at least 2-dimensional. Got {self.ndim}-dimensional array")

# TODO add check if out.dtype == self.dtype
out = Array()
# NOTE conj support is removed because it is never used
safe_call(backend.get().af_transpose(ctypes.pointer(out.arr), self.arr, False))
return out

@property
def size(self) -> int:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And still, we won't have a case where size equals None, but making the return Optional will lead us to major logical code changes if we want to keep typing work.
Mypy doesn't implement it in their source code as well https://github.com/numpy/numpy/blob/6f3e1f458e04d13bdd56cff5669f9fd96a25fb66/numpy/array_api/_array_object.py#L1088
My point is to keep int as an expected return and not try to imagine cases that likely never happen in the future with None return.
Or, let's switch to TDD and add valid test case for None return and then fix typing in the source code 🤷‍♂️

"""
Number of elements in an array.

Returns
-------
out : int
Number of elements in an array

Note
----
- This must equal the product of the array's dimensions.
"""
# NOTE previously - elements()
out = c_dim_t(0)
safe_call(backend.get().af_get_elements(ctypes.pointer(out), self.arr))
return out.value

@property
def ndim(self) -> int:
"""
Number of array dimensions (axes).

out : int
Number of array dimensions (axes).
"""
out = ctypes.c_uint(0)
safe_call(backend.get().af_get_numdims(ctypes.pointer(out), self.arr))
return out.value

@property
def shape(self) -> ShapeType:
"""
Return the shape of the array as a tuple.
Array dimensions.

Returns
-------
out : tuple[int, ...]
Array dimensions.
"""
# TODO refactor
d0 = c_dim_t(0)
Expand Down
Loading