array-api-jit 0.0.0__tar.gz → 0.1.0__tar.gz
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-0.0.0/src/array_api_jit.egg-info → array_api_jit-0.1.0}/PKG-INFO +48 -4
- {array_api_jit-0.0.0 → array_api_jit-0.1.0}/README.md +43 -1
- {array_api_jit-0.0.0 → array_api_jit-0.1.0}/pyproject.toml +10 -7
- array_api_jit-0.1.0/src/array_api_jit/__init__.py +4 -0
- array_api_jit-0.1.0/src/array_api_jit/_main.py +178 -0
- {array_api_jit-0.0.0 → array_api_jit-0.1.0/src/array_api_jit.egg-info}/PKG-INFO +48 -4
- {array_api_jit-0.0.0 → array_api_jit-0.1.0}/src/array_api_jit.egg-info/SOURCES.txt +2 -1
- array_api_jit-0.1.0/src/array_api_jit.egg-info/requires.txt +3 -0
- array_api_jit-0.1.0/tests/test_main.py +133 -0
- array_api_jit-0.0.0/src/array_api_jit/__init__.py +0 -1
- array_api_jit-0.0.0/src/array_api_jit/main.py +0 -3
- array_api_jit-0.0.0/tests/test_main.py +0 -6
- {array_api_jit-0.0.0 → array_api_jit-0.1.0}/LICENSE +0 -0
- {array_api_jit-0.0.0 → array_api_jit-0.1.0}/setup.cfg +0 -0
- {array_api_jit-0.0.0 → array_api_jit-0.1.0}/setup.py +0 -0
- {array_api_jit-0.0.0 → array_api_jit-0.1.0}/src/array_api_jit/py.typed +0 -0
- {array_api_jit-0.0.0 → array_api_jit-0.1.0}/src/array_api_jit.egg-info/dependency_links.txt +0 -0
- {array_api_jit-0.0.0 → array_api_jit-0.1.0}/src/array_api_jit.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: array-api-jit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.1.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,17 @@ 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.
|
|
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
|
+
Requires-Dist: cm-time>=0.1.2
|
|
25
|
+
Requires-Dist: frozendict>=2.4.6
|
|
24
26
|
Dynamic: license-file
|
|
25
27
|
|
|
26
28
|
# array API JIT
|
|
@@ -69,7 +71,49 @@ JIT decorator supporting multiple array API compatible libraries
|
|
|
69
71
|
|
|
70
72
|
Install this via pip (or your favourite package manager):
|
|
71
73
|
|
|
72
|
-
|
|
74
|
+
```shell
|
|
75
|
+
pip install array-api-jit
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Usage
|
|
79
|
+
|
|
80
|
+
Simply decorate your function with `@jit()`:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from array_api_jit import jit
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@jit()
|
|
87
|
+
def my_function(x: Any) -> Any:
|
|
88
|
+
xp = array_namespace(x)
|
|
89
|
+
return xp.sin(x) + xp.cos(x)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Advanced Usage
|
|
93
|
+
|
|
94
|
+
You can specify the decorator, arguments, and keyword arguments for each library.
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from array_api_jit import jit
|
|
98
|
+
from array_api_compat import array_namespace
|
|
99
|
+
from typing import Any
|
|
100
|
+
import numba
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@jit(
|
|
104
|
+
{"numpy": numba.jit()}, # numba.jit is not used by default because it may not succeed
|
|
105
|
+
decorator_kwargs={
|
|
106
|
+
"jax": {"static_argnames": ["n"]}
|
|
107
|
+
}, # jax requires for-loop variable to be "static_argnames"
|
|
108
|
+
# fail_on_error: bool = False, # do not raise an error if the decorator fails (Default)
|
|
109
|
+
# rerun_on_error: bool = True, # re-run the original function if the wrapped function fails (NOT Default)
|
|
110
|
+
)
|
|
111
|
+
def sin_n_times(x: Any, n: int) -> Any:
|
|
112
|
+
xp = array_namespace(x)
|
|
113
|
+
for i in range(n):
|
|
114
|
+
x = xp.sin(x)
|
|
115
|
+
return x
|
|
116
|
+
```
|
|
73
117
|
|
|
74
118
|
## Contributors ✨
|
|
75
119
|
|
|
@@ -44,7 +44,49 @@ JIT decorator supporting multiple array API compatible libraries
|
|
|
44
44
|
|
|
45
45
|
Install this via pip (or your favourite package manager):
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
```shell
|
|
48
|
+
pip install array-api-jit
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
Simply decorate your function with `@jit()`:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from array_api_jit import jit
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@jit()
|
|
60
|
+
def my_function(x: Any) -> Any:
|
|
61
|
+
xp = array_namespace(x)
|
|
62
|
+
return xp.sin(x) + xp.cos(x)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Advanced Usage
|
|
66
|
+
|
|
67
|
+
You can specify the decorator, arguments, and keyword arguments for each library.
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from array_api_jit import jit
|
|
71
|
+
from array_api_compat import array_namespace
|
|
72
|
+
from typing import Any
|
|
73
|
+
import numba
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@jit(
|
|
77
|
+
{"numpy": numba.jit()}, # numba.jit is not used by default because it may not succeed
|
|
78
|
+
decorator_kwargs={
|
|
79
|
+
"jax": {"static_argnames": ["n"]}
|
|
80
|
+
}, # jax requires for-loop variable to be "static_argnames"
|
|
81
|
+
# fail_on_error: bool = False, # do not raise an error if the decorator fails (Default)
|
|
82
|
+
# rerun_on_error: bool = True, # re-run the original function if the wrapped function fails (NOT Default)
|
|
83
|
+
)
|
|
84
|
+
def sin_n_times(x: Any, n: int) -> Any:
|
|
85
|
+
xp = array_namespace(x)
|
|
86
|
+
for i in range(n):
|
|
87
|
+
x = xp.sin(x)
|
|
88
|
+
return x
|
|
89
|
+
```
|
|
48
90
|
|
|
49
91
|
## Contributors ✨
|
|
50
92
|
|
|
@@ -4,20 +4,19 @@ requires = [ "setuptools" ]
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "array-api-jit"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.1.0"
|
|
8
8
|
description = "JIT decorator supporting multiple array API compatible libraries"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
11
11
|
authors = [
|
|
12
12
|
{ name = "34j", email = "34j.95a2p@simplelogin.com" },
|
|
13
13
|
]
|
|
14
|
-
requires-python = ">=3.
|
|
14
|
+
requires-python = ">=3.10"
|
|
15
15
|
classifiers = [
|
|
16
16
|
"Development Status :: 2 - Pre-Alpha",
|
|
17
17
|
"Intended Audience :: Developers",
|
|
18
18
|
"Natural Language :: English",
|
|
19
19
|
"Operating System :: OS Independent",
|
|
20
|
-
"Programming Language :: Python :: 3.9",
|
|
21
20
|
"Programming Language :: Python :: 3.10",
|
|
22
21
|
"Programming Language :: Python :: 3.11",
|
|
23
22
|
"Programming Language :: Python :: 3.12",
|
|
@@ -26,6 +25,9 @@ classifiers = [
|
|
|
26
25
|
]
|
|
27
26
|
|
|
28
27
|
dependencies = [
|
|
28
|
+
"array-api-compat>=1.11.2",
|
|
29
|
+
"cm-time>=0.1.2",
|
|
30
|
+
"frozendict>=2.4.6",
|
|
29
31
|
]
|
|
30
32
|
urls."Bug Tracker" = "https://github.com/34j/array-api-jit/issues"
|
|
31
33
|
urls.Changelog = "https://github.com/34j/array-api-jit/blob/main/CHANGELOG.md"
|
|
@@ -34,8 +36,11 @@ urls.repository = "https://github.com/34j/array-api-jit"
|
|
|
34
36
|
|
|
35
37
|
[dependency-groups]
|
|
36
38
|
dev = [
|
|
39
|
+
"jax>=0.4.30",
|
|
40
|
+
"numba>=0.60.0",
|
|
37
41
|
"pytest>=8,<9",
|
|
38
42
|
"pytest-cov>=6,<7",
|
|
43
|
+
"torch>=2.7.1",
|
|
39
44
|
]
|
|
40
45
|
docs = [
|
|
41
46
|
"furo>=2023.5.20; python_version>='3.11'",
|
|
@@ -45,7 +50,8 @@ docs = [
|
|
|
45
50
|
]
|
|
46
51
|
|
|
47
52
|
[tool.ruff]
|
|
48
|
-
|
|
53
|
+
format.docstring-code-format = true
|
|
54
|
+
line-length = 100
|
|
49
55
|
lint.select = [
|
|
50
56
|
"B", # flake8-bugbear
|
|
51
57
|
"D", # flake8-docstrings
|
|
@@ -83,9 +89,6 @@ lint.isort.known-first-party = [ "array_api_jit", "tests" ]
|
|
|
83
89
|
addopts = """\
|
|
84
90
|
-v
|
|
85
91
|
-Wdefault
|
|
86
|
-
--cov=array_api_jit
|
|
87
|
-
--cov-report=term
|
|
88
|
-
--cov-report=xml
|
|
89
92
|
"""
|
|
90
93
|
pythonpath = [ "src" ]
|
|
91
94
|
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# https://github.com/search?q=gumerov+translation+language%3APython&type=code&l=Python
|
|
2
|
+
import importlib.util
|
|
3
|
+
import warnings
|
|
4
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
5
|
+
from functools import cache, wraps
|
|
6
|
+
from types import ModuleType
|
|
7
|
+
from typing import Any, ParamSpec, TypeVar
|
|
8
|
+
|
|
9
|
+
from array_api_compat import (
|
|
10
|
+
array_namespace,
|
|
11
|
+
is_cupy_namespace,
|
|
12
|
+
is_dask_namespace,
|
|
13
|
+
is_jax_namespace,
|
|
14
|
+
is_numpy_namespace,
|
|
15
|
+
is_torch_namespace,
|
|
16
|
+
)
|
|
17
|
+
from frozendict import frozendict
|
|
18
|
+
|
|
19
|
+
if importlib.util.find_spec("numba"):
|
|
20
|
+
import numpy as np
|
|
21
|
+
from numba.extending import overload
|
|
22
|
+
|
|
23
|
+
@overload(array_namespace)
|
|
24
|
+
def _array_namespace_overload(*args: Any) -> Any:
|
|
25
|
+
def inner(*args: Any) -> Any:
|
|
26
|
+
return np
|
|
27
|
+
|
|
28
|
+
return inner
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
P = ParamSpec("P")
|
|
32
|
+
T = TypeVar("T")
|
|
33
|
+
Pin = ParamSpec("Pin")
|
|
34
|
+
Tin = TypeVar("Tin")
|
|
35
|
+
Pinner = ParamSpec("Pinner")
|
|
36
|
+
Tinner = TypeVar("Tinner")
|
|
37
|
+
STR_TO_IS_NAMESPACE = {
|
|
38
|
+
"numpy": is_numpy_namespace,
|
|
39
|
+
"jax": is_jax_namespace,
|
|
40
|
+
"cupy": is_cupy_namespace,
|
|
41
|
+
"torch": is_torch_namespace,
|
|
42
|
+
"dask": is_dask_namespace,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _default_decorator(
|
|
47
|
+
module: ModuleType,
|
|
48
|
+
/,
|
|
49
|
+
) -> Callable[[Callable[P, T]], Callable[P, T]]:
|
|
50
|
+
if is_jax_namespace(module):
|
|
51
|
+
import jax
|
|
52
|
+
|
|
53
|
+
return jax.jit
|
|
54
|
+
elif is_numpy_namespace(module) or is_cupy_namespace(module):
|
|
55
|
+
# import numba
|
|
56
|
+
|
|
57
|
+
# return numba.jit()
|
|
58
|
+
# The success rate of numba.jit is low
|
|
59
|
+
return lambda x: x
|
|
60
|
+
elif is_torch_namespace(module):
|
|
61
|
+
import torch
|
|
62
|
+
|
|
63
|
+
return torch.compile
|
|
64
|
+
elif is_dask_namespace(module):
|
|
65
|
+
return lambda x: x
|
|
66
|
+
else:
|
|
67
|
+
return getattr(module, "jit", lambda x: x)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
Decorator = Callable[[Callable[Pin, Tin]], Callable[Pin, Tin]]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def jit(
|
|
74
|
+
decorator: Mapping[str, Decorator[..., Any]] | None = None,
|
|
75
|
+
/,
|
|
76
|
+
*,
|
|
77
|
+
fail_on_error: bool = False,
|
|
78
|
+
rerun_on_error: bool = False,
|
|
79
|
+
decorator_args: Mapping[str, Sequence[Any]] | None = None,
|
|
80
|
+
decorator_kwargs: Mapping[str, Mapping[str, Any]] | None = None,
|
|
81
|
+
) -> Callable[[Callable[P, T]], Callable[P, T]]:
|
|
82
|
+
"""
|
|
83
|
+
Just-in-time compilation decorator with multiple backends.
|
|
84
|
+
|
|
85
|
+
Parameters
|
|
86
|
+
----------
|
|
87
|
+
decorator : Mapping[str, Callable[[Callable[P, T]], Callable[P, T]]] | None, optional
|
|
88
|
+
The JIT decorator to use for each array namespace, by default None
|
|
89
|
+
fail_on_error : bool, optional
|
|
90
|
+
If True, raise an error if the JIT decorator fails to apply.
|
|
91
|
+
If False, just warn and return the original function, by default False
|
|
92
|
+
rerun_on_error : bool, optional
|
|
93
|
+
If True, rerun the function without JIT if the function
|
|
94
|
+
with JIT applied fails, by default False
|
|
95
|
+
decorator_args : Mapping[str, Sequence[Any]] | None, optional
|
|
96
|
+
Additional positional arguments to be passed along with the function
|
|
97
|
+
to the decorator for each array namespace, by default None
|
|
98
|
+
decorator_kwargs : Mapping[str, Mapping[str, Any]] | None, optional
|
|
99
|
+
Additional keyword arguments to be passed along with the function
|
|
100
|
+
to the decorator for each array namespace, by default None
|
|
101
|
+
|
|
102
|
+
Returns
|
|
103
|
+
-------
|
|
104
|
+
Callable[[Callable[P, T]], Callable[P, T]]
|
|
105
|
+
The JIT decorator that can be applied to a function.
|
|
106
|
+
|
|
107
|
+
Example
|
|
108
|
+
-------
|
|
109
|
+
>>> from array_api_jit import jit
|
|
110
|
+
>>> from array_api_compat import array_namespace
|
|
111
|
+
>>> from typing import Any
|
|
112
|
+
>>> import numba
|
|
113
|
+
>>> @jit(
|
|
114
|
+
... {"numpy": numba.jit()}, # numba.jit is not used by default
|
|
115
|
+
... decorator_kwargs={"jax": {"static_argnames": ["n"]}}, # jax requires static_argnames
|
|
116
|
+
... )
|
|
117
|
+
... def sin_n_times(x: Any, n: int) -> Any:
|
|
118
|
+
... xp = array_namespace(x)
|
|
119
|
+
... for i in range(n):
|
|
120
|
+
... x = xp.sin(x)
|
|
121
|
+
... return x
|
|
122
|
+
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
def new_decorator(f: Callable[Pinner, Tinner]) -> Callable[Pinner, Tinner]:
|
|
126
|
+
decorator_args_ = frozendict(decorator_args or {})
|
|
127
|
+
decorator_kwargs_ = frozendict(decorator_kwargs or {})
|
|
128
|
+
decorator_ = decorator or {}
|
|
129
|
+
|
|
130
|
+
@cache
|
|
131
|
+
def jit_cached(xp: ModuleType) -> Callable[Pinner, Tinner]:
|
|
132
|
+
for name_, is_namespace in STR_TO_IS_NAMESPACE.items():
|
|
133
|
+
if is_namespace(xp):
|
|
134
|
+
name = name_
|
|
135
|
+
else:
|
|
136
|
+
name = xp.__name__.split(".")[0]
|
|
137
|
+
decorator_args__ = decorator_args_.get(name, ())
|
|
138
|
+
decorator_kwargs__ = decorator_kwargs_.get(name, {})
|
|
139
|
+
if name in decorator_:
|
|
140
|
+
decorator_current = decorator_[name]
|
|
141
|
+
else:
|
|
142
|
+
decorator_current = _default_decorator(xp)
|
|
143
|
+
try:
|
|
144
|
+
return decorator_current(f, *decorator_args__, **decorator_kwargs__)
|
|
145
|
+
except Exception as e:
|
|
146
|
+
if fail_on_error:
|
|
147
|
+
raise RuntimeError(f"Failed to apply JIT decorator for {name}") from e
|
|
148
|
+
warnings.warn(
|
|
149
|
+
f"Failed to apply JIT decorator for {name}: {e}",
|
|
150
|
+
RuntimeWarning,
|
|
151
|
+
stacklevel=2,
|
|
152
|
+
)
|
|
153
|
+
return f
|
|
154
|
+
|
|
155
|
+
@wraps(f)
|
|
156
|
+
def inner(*args_inner: Pinner.args, **kwargs_inner: Pinner.kwargs) -> Tinner:
|
|
157
|
+
try:
|
|
158
|
+
xp = array_namespace(*args_inner)
|
|
159
|
+
except TypeError as e:
|
|
160
|
+
if e.args[0] == "Unrecognized array input":
|
|
161
|
+
return f(*args_inner, **kwargs_inner)
|
|
162
|
+
raise
|
|
163
|
+
f_jit = jit_cached(xp)
|
|
164
|
+
try:
|
|
165
|
+
return f_jit(*args_inner, **kwargs_inner)
|
|
166
|
+
except Exception as e:
|
|
167
|
+
if rerun_on_error:
|
|
168
|
+
warnings.warn(
|
|
169
|
+
f"JIT failed for {xp.__name__}: {e}. Rerunning without JIT.",
|
|
170
|
+
RuntimeWarning,
|
|
171
|
+
stacklevel=2,
|
|
172
|
+
)
|
|
173
|
+
return f(*args_inner, **kwargs_inner)
|
|
174
|
+
raise RuntimeError(f"Failed to run JIT function for {xp.__name__}") from e
|
|
175
|
+
|
|
176
|
+
return inner
|
|
177
|
+
|
|
178
|
+
return new_decorator
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: array-api-jit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.1.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,17 @@ 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.
|
|
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
|
+
Requires-Dist: cm-time>=0.1.2
|
|
25
|
+
Requires-Dist: frozendict>=2.4.6
|
|
24
26
|
Dynamic: license-file
|
|
25
27
|
|
|
26
28
|
# array API JIT
|
|
@@ -69,7 +71,49 @@ JIT decorator supporting multiple array API compatible libraries
|
|
|
69
71
|
|
|
70
72
|
Install this via pip (or your favourite package manager):
|
|
71
73
|
|
|
72
|
-
|
|
74
|
+
```shell
|
|
75
|
+
pip install array-api-jit
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Usage
|
|
79
|
+
|
|
80
|
+
Simply decorate your function with `@jit()`:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from array_api_jit import jit
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@jit()
|
|
87
|
+
def my_function(x: Any) -> Any:
|
|
88
|
+
xp = array_namespace(x)
|
|
89
|
+
return xp.sin(x) + xp.cos(x)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Advanced Usage
|
|
93
|
+
|
|
94
|
+
You can specify the decorator, arguments, and keyword arguments for each library.
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from array_api_jit import jit
|
|
98
|
+
from array_api_compat import array_namespace
|
|
99
|
+
from typing import Any
|
|
100
|
+
import numba
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@jit(
|
|
104
|
+
{"numpy": numba.jit()}, # numba.jit is not used by default because it may not succeed
|
|
105
|
+
decorator_kwargs={
|
|
106
|
+
"jax": {"static_argnames": ["n"]}
|
|
107
|
+
}, # jax requires for-loop variable to be "static_argnames"
|
|
108
|
+
# fail_on_error: bool = False, # do not raise an error if the decorator fails (Default)
|
|
109
|
+
# rerun_on_error: bool = True, # re-run the original function if the wrapped function fails (NOT Default)
|
|
110
|
+
)
|
|
111
|
+
def sin_n_times(x: Any, n: int) -> Any:
|
|
112
|
+
xp = array_namespace(x)
|
|
113
|
+
for i in range(n):
|
|
114
|
+
x = xp.sin(x)
|
|
115
|
+
return x
|
|
116
|
+
```
|
|
73
117
|
|
|
74
118
|
## Contributors ✨
|
|
75
119
|
|
|
@@ -3,10 +3,11 @@ README.md
|
|
|
3
3
|
pyproject.toml
|
|
4
4
|
setup.py
|
|
5
5
|
src/array_api_jit/__init__.py
|
|
6
|
-
src/array_api_jit/
|
|
6
|
+
src/array_api_jit/_main.py
|
|
7
7
|
src/array_api_jit/py.typed
|
|
8
8
|
src/array_api_jit.egg-info/PKG-INFO
|
|
9
9
|
src/array_api_jit.egg-info/SOURCES.txt
|
|
10
10
|
src/array_api_jit.egg-info/dependency_links.txt
|
|
11
|
+
src/array_api_jit.egg-info/requires.txt
|
|
11
12
|
src/array_api_jit.egg-info/top_level.txt
|
|
12
13
|
tests/test_main.py
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
from os import environ
|
|
2
|
+
from sys import platform
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import numba
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pytest
|
|
8
|
+
from array_api_compat import array_namespace
|
|
9
|
+
from cm_time import timer
|
|
10
|
+
from numba import prange
|
|
11
|
+
from numba.extending import overload
|
|
12
|
+
|
|
13
|
+
from array_api_jit import jit
|
|
14
|
+
|
|
15
|
+
IS_CI = environ.get("CI", "false").lower() in ("true", "1", "yes")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@overload(np.stack)
|
|
19
|
+
def stack(arrays, axis=0):
|
|
20
|
+
def inner(arrays, axis=0):
|
|
21
|
+
if axis == 0:
|
|
22
|
+
shape = (len(arrays),) + arrays[0].shape # noqa: RUF005
|
|
23
|
+
stacked_array = np.empty(shape, dtype=arrays[0].dtype)
|
|
24
|
+
for j in prange(len(arrays)):
|
|
25
|
+
stacked_array[j] = arrays[j]
|
|
26
|
+
elif axis == -1:
|
|
27
|
+
shape = arrays[0].shape + (len(arrays),) # noqa: RUF005
|
|
28
|
+
stacked_array = np.empty(shape, dtype=arrays[0].dtype)
|
|
29
|
+
for j in prange(len(arrays)):
|
|
30
|
+
stacked_array[..., j] = arrays[j]
|
|
31
|
+
return stacked_array
|
|
32
|
+
|
|
33
|
+
return inner
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def legendre(x: Any, n_end: int) -> Any:
|
|
37
|
+
"""
|
|
38
|
+
Legendre polynomial of order 0 to n_end-1.
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
x : Any
|
|
43
|
+
The points at which to evaluate the polynomial.
|
|
44
|
+
n_end : int
|
|
45
|
+
The number of orders to compute, starting from 0.
|
|
46
|
+
|
|
47
|
+
Returns
|
|
48
|
+
-------
|
|
49
|
+
Any
|
|
50
|
+
Array of shape (*x.shape, n_end),
|
|
51
|
+
where [..., i] is the value of the i-th order polynomial at x.
|
|
52
|
+
|
|
53
|
+
"""
|
|
54
|
+
xp = array_namespace(x)
|
|
55
|
+
prevprev = xp.zeros_like(x)
|
|
56
|
+
prev = xp.ones_like(x)
|
|
57
|
+
result = [prevprev, prev]
|
|
58
|
+
for n in range(2, n_end):
|
|
59
|
+
prevprev, prev = prev, (((2 * n - 1) * x * prev - (n - 1) * prevprev) / n)
|
|
60
|
+
result.append(prev)
|
|
61
|
+
return xp.stack(result[:n_end], axis=-1)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def legendre_assign(x: Any, n_end: int) -> Any:
|
|
65
|
+
"""
|
|
66
|
+
Legendre polynomial of order 0 to n_end-1.
|
|
67
|
+
|
|
68
|
+
Parameters
|
|
69
|
+
----------
|
|
70
|
+
x : Any
|
|
71
|
+
The points at which to evaluate the polynomial.
|
|
72
|
+
n_end : int
|
|
73
|
+
The number of orders to compute, starting from 0.
|
|
74
|
+
|
|
75
|
+
Returns
|
|
76
|
+
-------
|
|
77
|
+
Any
|
|
78
|
+
Array of shape (*x.shape, n_end),
|
|
79
|
+
where [..., i] is the value of the i-th order polynomial at x.
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
xp = array_namespace(x)
|
|
83
|
+
prevprev = xp.zeros_like(x)
|
|
84
|
+
prev = xp.ones_like(x)
|
|
85
|
+
result = xp.empty((*x.shape, n_end), dtype=x.dtype)
|
|
86
|
+
if n_end > 0:
|
|
87
|
+
result[..., 0] = prevprev
|
|
88
|
+
if n_end > 1:
|
|
89
|
+
result[..., 1] = prev
|
|
90
|
+
for n in range(2, n_end):
|
|
91
|
+
prevprev, prev = prev, (((2 * n - 1) * x * prev - (n - 1) * prevprev) / n)
|
|
92
|
+
result[..., n] = prev
|
|
93
|
+
return result
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
legendre_jit = jit(
|
|
97
|
+
{"numpy": numba.jit(nogil=True, parallel=True)},
|
|
98
|
+
decorator_kwargs={"jax": {"static_argnames": ["n_end"]}},
|
|
99
|
+
)(legendre)
|
|
100
|
+
legendre_assign_jit = jit({"numpy": numba.jit(nogil=True, parallel=True)})(legendre_assign)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@pytest.mark.skipif(
|
|
104
|
+
IS_CI and platform != "linux", reason="Compiler not available on GitHub Actions"
|
|
105
|
+
)
|
|
106
|
+
def test_jit(xp: Any) -> None:
|
|
107
|
+
t = {}
|
|
108
|
+
for name, func in [("nojit", legendre), ("jit", legendre_jit)] + (
|
|
109
|
+
[
|
|
110
|
+
("assign-nojit", legendre_assign),
|
|
111
|
+
("assign-jit", legendre_assign_jit),
|
|
112
|
+
]
|
|
113
|
+
if "jax" not in xp.__name__
|
|
114
|
+
else []
|
|
115
|
+
):
|
|
116
|
+
print(xp.__name__, name)
|
|
117
|
+
for i in range(20):
|
|
118
|
+
with timer() as timer_:
|
|
119
|
+
x = xp.arange(1000000, dtype=xp.float32)
|
|
120
|
+
p = func(x, 10)
|
|
121
|
+
assert p.shape == (1000000, 10)
|
|
122
|
+
t[name] = timer_.elapsed
|
|
123
|
+
if i == 0:
|
|
124
|
+
print(f"First call: {timer_.elapsed:g}s")
|
|
125
|
+
print(f"Last call: {timer_.elapsed:g}s")
|
|
126
|
+
if "numpy" not in xp.__name__:
|
|
127
|
+
assert t["jit"] < t["nojit"], (
|
|
128
|
+
f"JIT time {t['jit']} should be less than non-JIT time {t['nojit']}"
|
|
129
|
+
)
|
|
130
|
+
assert t["assign-jit"] < t["assign-nojit"], (
|
|
131
|
+
f"JIT assign time {t['assign-jit']} should be "
|
|
132
|
+
f"less than non-JIT assign time {t['assign-nojit']}"
|
|
133
|
+
)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.0.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|