array-api-jit 0.0.0__py3-none-any.whl → 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
array_api_jit/__init__.py CHANGED
@@ -1 +1,4 @@
1
- __version__ = "0.0.0"
1
+ __version__ = "1.0.0"
2
+ from ._main import jit
3
+
4
+ __all__ = ["jit"]
array_api_jit/_main.py ADDED
@@ -0,0 +1,176 @@
1
+ import importlib.util
2
+ import warnings
3
+ from collections.abc import Callable, Mapping, Sequence
4
+ from functools import cache, wraps
5
+ from types import ModuleType
6
+ from typing import Any, ParamSpec, TypeVar
7
+
8
+ from array_api_compat import (
9
+ array_namespace,
10
+ is_cupy_namespace,
11
+ is_dask_namespace,
12
+ is_jax_namespace,
13
+ is_numpy_namespace,
14
+ is_torch_namespace,
15
+ )
16
+
17
+ if importlib.util.find_spec("numba"):
18
+ import numpy as np
19
+ from numba.extending import overload
20
+
21
+ @overload(array_namespace)
22
+ def _array_namespace_overload(*args: Any) -> Any:
23
+ def inner(*args: Any) -> Any:
24
+ return np
25
+
26
+ return inner
27
+
28
+
29
+ P = ParamSpec("P")
30
+ T = TypeVar("T")
31
+ Pin = ParamSpec("Pin")
32
+ Tin = TypeVar("Tin")
33
+ Pinner = ParamSpec("Pinner")
34
+ Tinner = TypeVar("Tinner")
35
+ STR_TO_IS_NAMESPACE = {
36
+ "numpy": is_numpy_namespace,
37
+ "jax": is_jax_namespace,
38
+ "cupy": is_cupy_namespace,
39
+ "torch": is_torch_namespace,
40
+ "dask": is_dask_namespace,
41
+ }
42
+
43
+
44
+ def _default_decorator(
45
+ module: ModuleType,
46
+ /,
47
+ ) -> Callable[[Callable[P, T]], Callable[P, T]]:
48
+ if is_jax_namespace(module):
49
+ import jax
50
+
51
+ return jax.jit
52
+ elif is_numpy_namespace(module) or is_cupy_namespace(module):
53
+ # import numba
54
+
55
+ # return numba.jit()
56
+ # The success rate of numba.jit is low
57
+ return lambda x: x
58
+ elif is_torch_namespace(module):
59
+ import torch
60
+
61
+ return torch.compile
62
+ elif is_dask_namespace(module):
63
+ return lambda x: x
64
+ else:
65
+ return getattr(module, "jit", lambda x: x)
66
+
67
+
68
+ Decorator = Callable[[Callable[Pin, Tin]], Callable[Pin, Tin]]
69
+
70
+
71
+ def jit(
72
+ decorator: Mapping[str, Decorator[..., Any]] | None = None,
73
+ /,
74
+ *,
75
+ fail_on_error: bool = False,
76
+ rerun_on_error: bool = False,
77
+ decorator_args: Mapping[str, Sequence[Any]] | None = None,
78
+ decorator_kwargs: Mapping[str, Mapping[str, Any]] | None = None,
79
+ ) -> Callable[[Callable[P, T]], Callable[P, T]]:
80
+ """
81
+ Just-in-time compilation decorator with multiple backends.
82
+
83
+ Parameters
84
+ ----------
85
+ decorator : Mapping[str, Callable[[Callable[P, T]], Callable[P, T]]] | None, optional
86
+ The JIT decorator to use for each array namespace, by default None
87
+ fail_on_error : bool, optional
88
+ If True, raise an error if the JIT decorator fails to apply.
89
+ If False, just warn and return the original function, by default False
90
+ rerun_on_error : bool, optional
91
+ If True, rerun the function without JIT if the function
92
+ with JIT applied fails, by default False
93
+ decorator_args : Mapping[str, Sequence[Any]] | None, optional
94
+ Additional positional arguments to be passed along with the function
95
+ to the decorator for each array namespace, by default None
96
+ decorator_kwargs : Mapping[str, Mapping[str, Any]] | None, optional
97
+ Additional keyword arguments to be passed along with the function
98
+ to the decorator for each array namespace, by default None
99
+
100
+ Returns
101
+ -------
102
+ Callable[[Callable[P, T]], Callable[P, T]]
103
+ The JIT decorator that can be applied to a function.
104
+
105
+ Example
106
+ -------
107
+ >>> from array_api_jit import jit
108
+ >>> from array_api_compat import array_namespace
109
+ >>> from typing import Any
110
+ >>> import numba
111
+ >>> @jit(
112
+ ... {"numpy": numba.jit()}, # numba.jit is not used by default
113
+ ... decorator_kwargs={"jax": {"static_argnames": ["n"]}}, # jax requires static_argnames
114
+ ... )
115
+ ... def sin_n_times(x: Any, n: int) -> Any:
116
+ ... xp = array_namespace(x)
117
+ ... for i in range(n):
118
+ ... x = xp.sin(x)
119
+ ... return x
120
+
121
+ """
122
+
123
+ def new_decorator(f: Callable[Pinner, Tinner]) -> Callable[Pinner, Tinner]:
124
+ decorator_args_ = decorator_args or {}
125
+ decorator_kwargs_ = decorator_kwargs or {}
126
+ decorator_ = decorator or {}
127
+
128
+ @cache
129
+ def jit_cached(xp: ModuleType) -> Callable[Pinner, Tinner]:
130
+ for name_, is_namespace in STR_TO_IS_NAMESPACE.items():
131
+ if is_namespace(xp):
132
+ name = name_
133
+ else:
134
+ name = xp.__name__.split(".")[0]
135
+ decorator_args__ = decorator_args_.get(name, ())
136
+ decorator_kwargs__ = decorator_kwargs_.get(name, {})
137
+ if name in decorator_:
138
+ decorator_current = decorator_[name]
139
+ else:
140
+ decorator_current = _default_decorator(xp)
141
+ try:
142
+ return decorator_current(f, *decorator_args__, **decorator_kwargs__)
143
+ except Exception as e:
144
+ if fail_on_error:
145
+ raise RuntimeError(f"Failed to apply JIT decorator for {name}") from e
146
+ warnings.warn(
147
+ f"Failed to apply JIT decorator for {name}: {e}",
148
+ RuntimeWarning,
149
+ stacklevel=2,
150
+ )
151
+ return f
152
+
153
+ @wraps(f)
154
+ def inner(*args_inner: Pinner.args, **kwargs_inner: Pinner.kwargs) -> Tinner:
155
+ try:
156
+ xp = array_namespace(*args_inner)
157
+ except TypeError as e:
158
+ if e.args[0] == "Unrecognized array input":
159
+ return f(*args_inner, **kwargs_inner)
160
+ raise
161
+ f_jit = jit_cached(xp)
162
+ try:
163
+ return f_jit(*args_inner, **kwargs_inner)
164
+ except Exception as e:
165
+ if rerun_on_error:
166
+ warnings.warn(
167
+ f"JIT failed for {xp.__name__}: {e}. Rerunning without JIT.",
168
+ RuntimeWarning,
169
+ stacklevel=2,
170
+ )
171
+ return f(*args_inner, **kwargs_inner)
172
+ raise RuntimeError(f"Failed to run JIT function for {xp.__name__}") from e
173
+
174
+ return inner
175
+
176
+ return new_decorator
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: array-api-jit
3
- Version: 0.0.0
3
+ Version: 1.0.0
4
4
  Summary: JIT decorator supporting multiple array API compatible libraries
5
5
  Author-email: 34j <34j.95a2p@simplelogin.com>
6
6
  License: MIT
@@ -12,15 +12,15 @@ Classifier: Development Status :: 2 - Pre-Alpha
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: Natural Language :: English
14
14
  Classifier: Operating System :: OS Independent
15
- Classifier: Programming Language :: Python :: 3.9
16
15
  Classifier: Programming Language :: Python :: 3.10
17
16
  Classifier: Programming Language :: Python :: 3.11
18
17
  Classifier: Programming Language :: Python :: 3.12
19
18
  Classifier: Programming Language :: Python :: 3.13
20
19
  Classifier: Topic :: Software Development :: Libraries
21
- Requires-Python: >=3.9
20
+ Requires-Python: >=3.10
22
21
  Description-Content-Type: text/markdown
23
22
  License-File: LICENSE
23
+ Requires-Dist: array-api-compat>=1.11.2
24
24
  Dynamic: license-file
25
25
 
26
26
  # array API JIT
@@ -69,7 +69,49 @@ JIT decorator supporting multiple array API compatible libraries
69
69
 
70
70
  Install this via pip (or your favourite package manager):
71
71
 
72
- `pip install array-api-jit`
72
+ ```shell
73
+ pip install array-api-jit
74
+ ```
75
+
76
+ ## Usage
77
+
78
+ Simply decorate your function with `@jit()`:
79
+
80
+ ```python
81
+ from array_api_jit import jit
82
+
83
+
84
+ @jit()
85
+ def my_function(x: Any) -> Any:
86
+ xp = array_namespace(x)
87
+ return xp.sin(x) + xp.cos(x)
88
+ ```
89
+
90
+ ## Advanced Usage
91
+
92
+ You can specify the decorator, arguments, and keyword arguments for each library.
93
+
94
+ ```python
95
+ from array_api_jit import jit
96
+ from array_api_compat import array_namespace
97
+ from typing import Any
98
+ import numba
99
+
100
+
101
+ @jit(
102
+ {"numpy": numba.jit()}, # numba.jit is not used by default because it may not succeed
103
+ decorator_kwargs={
104
+ "jax": {"static_argnames": ["n"]}
105
+ }, # jax requires for-loop variable to be "static_argnames"
106
+ # fail_on_error: bool = False, # do not raise an error if the decorator fails (Default)
107
+ # rerun_on_error: bool = True, # re-run the original function if the wrapped function fails (NOT Default)
108
+ )
109
+ def sin_n_times(x: Any, n: int) -> Any:
110
+ xp = array_namespace(x)
111
+ for i in range(n):
112
+ x = xp.sin(x)
113
+ return x
114
+ ```
73
115
 
74
116
  ## Contributors ✨
75
117
 
@@ -0,0 +1,8 @@
1
+ array_api_jit/__init__.py,sha256=6Cd5sMzZMC4jIJ97HL2eYIS8scDQonRkgK-uT221w_w,64
2
+ array_api_jit/_main.py,sha256=RrE4K4y2wO4zukuRrddfBoGrojv8b5XopzMFqh8ftSU,5862
3
+ array_api_jit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ array_api_jit-1.0.0.dist-info/licenses/LICENSE,sha256=Otwb96xcK4QBqXXVdBKpLZVnc4mJuFTa8rsMUEpphMM,1061
5
+ array_api_jit-1.0.0.dist-info/METADATA,sha256=9nup4sy6v_xHlno6M6HV4-1ZBCKiGxfINaruINoRnlI,5174
6
+ array_api_jit-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
+ array_api_jit-1.0.0.dist-info/top_level.txt,sha256=90tmkew6kqpBt5VZbfvK2cvsOfCn_oFZfDf74F-s7Hg,14
8
+ array_api_jit-1.0.0.dist-info/RECORD,,
array_api_jit/main.py DELETED
@@ -1,3 +0,0 @@
1
- def add(n1: int, n2: int) -> int:
2
- """Add the arguments."""
3
- return n1 + n2
@@ -1,8 +0,0 @@
1
- array_api_jit/__init__.py,sha256=ShXQBVjyiSOHxoQJS2BvNG395W4KZfqMxZWBAR0MZrE,22
2
- array_api_jit/main.py,sha256=j2h8YoaIyvMAosyxXlV3rlWEQ7x1yvnZWhdjVG4vXdU,82
3
- array_api_jit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- array_api_jit-0.0.0.dist-info/licenses/LICENSE,sha256=Otwb96xcK4QBqXXVdBKpLZVnc4mJuFTa8rsMUEpphMM,1061
5
- array_api_jit-0.0.0.dist-info/METADATA,sha256=45CZdjO99TuZj8qkFY43C6yCaE23YMd9-iW7IKL2Uhw,4182
6
- array_api_jit-0.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
7
- array_api_jit-0.0.0.dist-info/top_level.txt,sha256=90tmkew6kqpBt5VZbfvK2cvsOfCn_oFZfDf74F-s7Hg,14
8
- array_api_jit-0.0.0.dist-info/RECORD,,