galley-jl-python 0.1.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.
- galley_jl_python/__init__.py +361 -0
- galley_jl_python/_array_api_info.py +94 -0
- galley_jl_python/_sysimage.py +222 -0
- galley_jl_python/compiled.py +171 -0
- galley_jl_python/dtypes.py +61 -0
- galley_jl_python/einstein.py +561 -0
- galley_jl_python/errors.py +2 -0
- galley_jl_python/finch_fused.py +33 -0
- galley_jl_python/io.py +15 -0
- galley_jl_python/julia.py +37 -0
- galley_jl_python/juliapkg.json +386 -0
- galley_jl_python/juliapkg_dev.json +22 -0
- galley_jl_python/levels.py +110 -0
- galley_jl_python/linalg/__init__.py +3 -0
- galley_jl_python/linalg/_linalg.py +26 -0
- galley_jl_python/tensor.py +1739 -0
- galley_jl_python/typing.py +17 -0
- galley_jl_python-0.1.0.dist-info/METADATA +214 -0
- galley_jl_python-0.1.0.dist-info/RECORD +21 -0
- galley_jl_python-0.1.0.dist-info/WHEEL +4 -0
- galley_jl_python-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import abstractmethod
|
|
4
|
+
from collections.abc import Callable, Iterator
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from functools import wraps
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
|
+
|
|
9
|
+
from .julia import jl
|
|
10
|
+
from .typing import JuliaObj
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from .tensor import Tensor
|
|
14
|
+
|
|
15
|
+
IterObj = tuple | list | dict | Any
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _recurse(x: IterObj, /, *, f: Callable[[Any], Any]) -> IterObj:
|
|
19
|
+
if isinstance(x, tuple | list):
|
|
20
|
+
return type(x)(_recurse(xi, f=f) for xi in x)
|
|
21
|
+
if isinstance(x, dict):
|
|
22
|
+
ret = {k: _recurse(v, f=f) for k, v in x.items()}
|
|
23
|
+
if type(x) is not dict:
|
|
24
|
+
ret = type(x)(ret)
|
|
25
|
+
return ret
|
|
26
|
+
return f(x)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _recurse_iter(x: IterObj, /) -> Iterator[Any]:
|
|
30
|
+
if isinstance(x, tuple | list):
|
|
31
|
+
for xi in x:
|
|
32
|
+
yield from _recurse_iter(xi)
|
|
33
|
+
return
|
|
34
|
+
if isinstance(x, dict):
|
|
35
|
+
for xi in x.values():
|
|
36
|
+
yield from _recurse_iter(xi)
|
|
37
|
+
return
|
|
38
|
+
yield x
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _to_lazy_tensor(x: Tensor | Any, /) -> Tensor | Any:
|
|
42
|
+
from .tensor import Tensor
|
|
43
|
+
|
|
44
|
+
return x if not isinstance(x, Tensor) else lazy(x)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass
|
|
48
|
+
class _ArgumentIndexer:
|
|
49
|
+
_idx: int = 0
|
|
50
|
+
|
|
51
|
+
def index(self, _) -> int:
|
|
52
|
+
ret = self._idx
|
|
53
|
+
self._idx += 1
|
|
54
|
+
return ret
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _recurse_iter_compute(x: IterObj, /, *, compute_kwargs: dict[str, Any]) -> IterObj:
|
|
58
|
+
from .tensor import Tensor
|
|
59
|
+
|
|
60
|
+
# Make a recursive iterator of indices.
|
|
61
|
+
idx_obj = _recurse(x, f=_ArgumentIndexer().index)
|
|
62
|
+
jl_computed = []
|
|
63
|
+
py_computed = []
|
|
64
|
+
|
|
65
|
+
# Collect lazy tensors; use placeholder
|
|
66
|
+
_placeholder = object()
|
|
67
|
+
for xi in _recurse_iter(x):
|
|
68
|
+
if isinstance(xi, Tensor) and not xi.is_computed():
|
|
69
|
+
jl_computed.append(xi._obj)
|
|
70
|
+
py_computed.append(_placeholder)
|
|
71
|
+
else:
|
|
72
|
+
py_computed.append(xi)
|
|
73
|
+
jl_len = len(jl_computed)
|
|
74
|
+
# This doesn't return an iterable of arrays -- only a single array
|
|
75
|
+
# for `len(jl_computed) == 1`
|
|
76
|
+
jl_computed = jl.Finch.compute(*jl_computed, **compute_kwargs)
|
|
77
|
+
if jl_len == 1:
|
|
78
|
+
jl_computed = (jl_computed,)
|
|
79
|
+
|
|
80
|
+
# Replace placeholders with computed tensors.
|
|
81
|
+
jl_computed_iter = iter(jl_computed)
|
|
82
|
+
for i in range(len(py_computed)):
|
|
83
|
+
if py_computed[i] is _placeholder:
|
|
84
|
+
py_computed[i] = Tensor(next(jl_computed_iter))
|
|
85
|
+
|
|
86
|
+
# Replace recursive indices by actual computed objects
|
|
87
|
+
return _recurse(idx_obj, f=lambda idx: py_computed[idx])
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def compiled(opt=None, *, force_materialization=False, tag: int | None = None):
|
|
91
|
+
def inner(func):
|
|
92
|
+
@wraps(func)
|
|
93
|
+
def wrapper_func(*args, **kwargs):
|
|
94
|
+
from .tensor import Tensor
|
|
95
|
+
|
|
96
|
+
args = tuple(args)
|
|
97
|
+
kwargs = dict(kwargs)
|
|
98
|
+
compute_at_end = force_materialization or all(
|
|
99
|
+
t.is_computed()
|
|
100
|
+
for t in _recurse_iter((args, kwargs))
|
|
101
|
+
if isinstance(t, Tensor)
|
|
102
|
+
)
|
|
103
|
+
args = _recurse(args, f=_to_lazy_tensor)
|
|
104
|
+
kwargs = _recurse(kwargs, f=_to_lazy_tensor)
|
|
105
|
+
result = func(*args, **kwargs)
|
|
106
|
+
if not compute_at_end:
|
|
107
|
+
return result
|
|
108
|
+
compute_kwargs = (
|
|
109
|
+
{"ctx": opt.get_julia_scheduler()} if opt is not None else {}
|
|
110
|
+
)
|
|
111
|
+
if tag is not None:
|
|
112
|
+
compute_kwargs["tag"] = tag
|
|
113
|
+
|
|
114
|
+
return _recurse_iter_compute(result, compute_kwargs=compute_kwargs)
|
|
115
|
+
|
|
116
|
+
return wrapper_func
|
|
117
|
+
|
|
118
|
+
return inner
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class AbstractScheduler:
|
|
122
|
+
def __init__(self, verbose: bool = False):
|
|
123
|
+
self.verbose = verbose
|
|
124
|
+
|
|
125
|
+
@abstractmethod
|
|
126
|
+
def get_julia_scheduler(self) -> JuliaObj:
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class GalleyScheduler(AbstractScheduler):
|
|
131
|
+
def get_julia_scheduler(self) -> JuliaObj:
|
|
132
|
+
return jl.Finch.galley_scheduler(verbose=self.verbose)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class DefaultScheduler(AbstractScheduler):
|
|
136
|
+
def get_julia_scheduler(self) -> JuliaObj:
|
|
137
|
+
return jl.Finch.default_scheduler(verbose=self.verbose)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def set_optimizer(opt: AbstractScheduler) -> None:
|
|
141
|
+
jl.Finch.set_scheduler_b(opt.get_julia_scheduler())
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
set_optimizer(GalleyScheduler())
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def lazy(tensor: Tensor) -> Tensor:
|
|
148
|
+
from .tensor import Tensor
|
|
149
|
+
|
|
150
|
+
if tensor.is_computed():
|
|
151
|
+
return Tensor(jl.Finch.LazyTensor(tensor._obj))
|
|
152
|
+
return tensor
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def compute(
|
|
156
|
+
tensor: Tensor, *, opt: AbstractScheduler | None = None, tag: int = -1
|
|
157
|
+
) -> Tensor:
|
|
158
|
+
from .tensor import Tensor
|
|
159
|
+
|
|
160
|
+
if not tensor.is_computed():
|
|
161
|
+
if opt is None:
|
|
162
|
+
return Tensor(jl.Finch.compute(tensor._obj, tag=tag))
|
|
163
|
+
return Tensor(
|
|
164
|
+
jl.Finch.compute(
|
|
165
|
+
tensor._obj,
|
|
166
|
+
verbose=opt.verbose,
|
|
167
|
+
ctx=opt.get_julia_scheduler(),
|
|
168
|
+
tag=tag,
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
return tensor
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import builtins
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from .julia import jl
|
|
6
|
+
|
|
7
|
+
int_: jl.DataType = jl.Int
|
|
8
|
+
int8: jl.DataType = jl.Int8
|
|
9
|
+
int16: jl.DataType = jl.Int16
|
|
10
|
+
int32: jl.DataType = jl.Int32
|
|
11
|
+
int64: jl.DataType = jl.Int64
|
|
12
|
+
uint: jl.DataType = jl.UInt
|
|
13
|
+
uint8: jl.DataType = jl.UInt8
|
|
14
|
+
uint16: jl.DataType = jl.UInt16
|
|
15
|
+
uint32: jl.DataType = jl.UInt32
|
|
16
|
+
uint64: jl.DataType = jl.UInt64
|
|
17
|
+
float16: jl.DataType = jl.Float16
|
|
18
|
+
float32: jl.DataType = jl.Float32
|
|
19
|
+
float64: jl.DataType = jl.Float64
|
|
20
|
+
complex64: jl.DataType = jl.ComplexF32
|
|
21
|
+
complex128: jl.DataType = jl.ComplexF64
|
|
22
|
+
bool: jl.DataType = jl.Bool
|
|
23
|
+
|
|
24
|
+
number: jl.DataType = jl.Number
|
|
25
|
+
complex: jl.DataType = jl.Complex
|
|
26
|
+
integer: jl.DataType = jl.Integer
|
|
27
|
+
abstract_float: jl.DataType = jl.AbstractFloat
|
|
28
|
+
|
|
29
|
+
jl_to_np_dtype = {
|
|
30
|
+
int_: np.int_,
|
|
31
|
+
int8: np.int8,
|
|
32
|
+
int16: np.int16,
|
|
33
|
+
int32: np.int32,
|
|
34
|
+
int64: np.int64,
|
|
35
|
+
uint: np.uint,
|
|
36
|
+
uint8: np.uint8,
|
|
37
|
+
uint16: np.uint16,
|
|
38
|
+
uint32: np.uint32,
|
|
39
|
+
uint64: np.uint64,
|
|
40
|
+
float16: np.float16,
|
|
41
|
+
float32: np.float32,
|
|
42
|
+
float64: np.float64,
|
|
43
|
+
complex64: np.complex64,
|
|
44
|
+
complex128: np.complex128,
|
|
45
|
+
bool: builtins.bool,
|
|
46
|
+
None: None,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def finfo(dtype):
|
|
51
|
+
return np.finfo(jl_to_np_dtype[dtype])
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def iinfo(dtype):
|
|
55
|
+
return np.iinfo(jl_to_np_dtype[dtype])
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def can_cast(from_, to, /) -> builtins.bool:
|
|
59
|
+
if hasattr(from_, "dtype"):
|
|
60
|
+
from_ = from_.dtype
|
|
61
|
+
return np.can_cast(jl_to_np_dtype[from_], jl_to_np_dtype[to])
|