-
Notifications
You must be signed in to change notification settings - Fork 5
✨: HasX attributes #34
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
base: main
Are you sure you want to change the base?
Changes from all commits
3cb0930
d786599
4c3963b
58698bb
25db39c
7f1eb4e
a53c176
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,22 @@ | ||
__all__ = ( | ||
"Array", | ||
"HasArrayNamespace", | ||
"HasDType", | ||
"HasDevice", | ||
"HasMatrixTranspose", | ||
"HasNDim", | ||
"HasShape", | ||
"HasSize", | ||
"HasTranspose", | ||
) | ||
|
||
from types import ModuleType | ||
from typing import Literal, Protocol | ||
from typing import Literal, Protocol, Self | ||
from typing_extensions import TypeVar | ||
|
||
NamespaceT_co = TypeVar("NamespaceT_co", covariant=True, default=ModuleType) | ||
DTypeT_co = TypeVar("DTypeT_co", covariant=True) | ||
DeviceT_co = TypeVar("DeviceT_co", covariant=True, default=object) | ||
|
||
|
||
class HasArrayNamespace(Protocol[NamespaceT_co]): | ||
|
@@ -67,10 +75,125 @@ def dtype(self, /) -> DTypeT_co: | |
... | ||
|
||
|
||
class HasDevice(Protocol[DeviceT_co]): | ||
"""Protocol for array classes that have a device attribute.""" | ||
|
||
@property | ||
def device(self) -> DeviceT_co: | ||
"""Hardware device the array data resides on.""" | ||
... | ||
|
||
|
||
class HasMatrixTranspose(Protocol): | ||
"""Protocol for array classes that have a matrix transpose attribute.""" | ||
|
||
@property | ||
def mT(self) -> Self: # noqa: N802 | ||
"""Transpose of a matrix (or a stack of matrices). | ||
|
||
If an array instance has fewer than two dimensions, an error should be | ||
raised. | ||
|
||
Returns: | ||
Self: array whose last two dimensions (axes) are permuted in reverse | ||
order relative to original array (i.e., for an array instance | ||
having shape `(..., M, N)`, the returned array must have shape | ||
`(..., N, M))`. The returned array must have the same data type | ||
as the original array. | ||
|
||
""" | ||
... | ||
|
||
|
||
class HasNDim(Protocol): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a situation where you'd wanna use
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you're checking for the attribute There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, my point is that I think we should only provide users with protocols that help them annotate their array-api code. Otherwise, it'll just be confusing for the users, and a waste of time for us. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh. As the Array API is the intersection of the array libraries, IMO pretty much everything is useful. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea of course, everything in the array-api is designed for a reason. But all of those reasons apply in the runtime world. And our goal is to provide an API for the static-typing world, which is but a shadow of the runtime one, where only a subset of the API has practical use. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Python comes with batteries. My suggestion is to have all these low-level protocols. Where we go beyond the baseline Array API is in how these protocols are parametrized and how we build intersection types. This is where necessity drives us to make decisions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't disagree with this idea, but let's consider the situation. There are numerous array libraries which people have spent a long time working on, thinking about their APIs for many years. Numpy in particular sets much of the standard and has many people thinking about the things in it. They got together to rethink numpy and array libraries in general, to mass adoption. We've been to some of those meetings where they're agonizing over a small piece of API. IMO the presumption of usefulness is very clear. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jorenham can we get in protocols for the methods? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
What methods, exactly? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Methods and attributes of Array. |
||
"""Protocol for array classes that have a number of dimensions attribute.""" | ||
|
||
@property | ||
def ndim(self) -> int: | ||
"""Number of array dimensions (axes). | ||
|
||
Returns: | ||
int: number of array dimensions (axes). | ||
|
||
""" | ||
... | ||
|
||
|
||
class HasShape(Protocol): | ||
"""Protocol for array classes that have a shape attribute.""" | ||
|
||
@property | ||
def shape(self) -> tuple[int | None, ...]: | ||
jorenham marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"""Shape of the array. | ||
|
||
Returns: | ||
tuple[int | None, ...]: array dimensions. An array dimension must be None | ||
if and only if a dimension is unknown. | ||
|
||
Notes: | ||
For array libraries having graph-based computational models, array | ||
dimensions may be unknown due to data-dependent operations (e.g., | ||
boolean indexing; `A[:, B > 0]`) and thus cannot be statically | ||
resolved without knowing array contents. | ||
|
||
""" | ||
... | ||
|
||
|
||
class HasSize(Protocol): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the use-case of this one? Is there anything this can help with, that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't we have Protocols for all attributes in the Array API? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see why we should write a protocol if there's no use for it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. def is_sized(obj: Any, /) -> TypeGuard[HasSize]: ... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, and what would you use that for in a real-world scenario There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I know, and I wasn't suggesting anything like that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is that not the practical effect of omission? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the Array API spec has methods There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
No? AFAIK, this is library depends on the array api, not the other way around. But maybe I'm being naive here 🤷🏻. Plus, there are already stub-like types in the array-api itself (https://github.com/data-apis/array-api/tree/main/src/array_api_stubs) that are used for documentation purposes (and don't have any use-case besides that). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They aren't planning on publishing those. As I understand it, we depend on the Array API spec and our remit is to describe it statically. As the official typing library for that spec, omission is change. We have HasNamespace. We should have similar for the other methods and attributes of Array. We should have useful intersection types related to Array. We should have DoesFunction protocols for the functions and a Namespace protocol for their collection. |
||
"""Protocol for array classes that have a size attribute.""" | ||
|
||
@property | ||
def size(self) -> int | None: | ||
"""Number of elements in an array. | ||
|
||
Returns: | ||
int | None: number of elements in an array. The returned value must | ||
be `None` if and only if one or more array dimensions are | ||
unknown. | ||
|
||
Notes: | ||
This must equal the product of the array's dimensions. | ||
|
||
""" | ||
... | ||
|
||
|
||
class HasTranspose(Protocol): | ||
jorenham marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"""Protocol for array classes that support the transpose operation.""" | ||
|
||
@property | ||
def T(self) -> Self: # noqa: N802 | ||
"""Transpose of the array. | ||
|
||
The array instance must be two-dimensional. If the array instance is not | ||
two-dimensional, an error should be raised. | ||
|
||
Returns: | ||
Self: 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. | ||
|
||
Notes: | ||
Limiting the transpose to two-dimensional arrays (matrices) deviates | ||
from the NumPy et al practice of reversing all axes for arrays | ||
having more than two-dimensions. This is intentional, as reversing | ||
all axes was found to be problematic (e.g., conflicting with the | ||
mathematical definition of a transpose which is limited to matrices; | ||
not operating on batches of matrices; et cetera). In order to | ||
reverse all axes, one is recommended to use the functional | ||
`PermuteDims` interface found in this specification. | ||
|
||
""" | ||
... | ||
|
||
|
||
class Array( | ||
HasArrayNamespace[NamespaceT_co], | ||
# ------ Attributes ------- | ||
HasDType[DTypeT_co], | ||
# ------- Methods --------- | ||
HasArrayNamespace[NamespaceT_co], | ||
# ------------------------- | ||
Protocol[DTypeT_co, NamespaceT_co], | ||
): | ||
|
Uh oh!
There was an error while loading. Please reload this page.