torch-pyodide 0.0.1__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.
@@ -0,0 +1,13 @@
1
+ Metadata-Version: 2.4
2
+ Name: torch-pyodide
3
+ Version: 0.0.1
4
+ Summary: Minimal torch-like API for Pyodide using WebGPU runtime bridge.
5
+ Project-URL: Homepage, https://github.com/celsowm/torch-pyodide
6
+ Project-URL: Repository, https://github.com/celsowm/torch-pyodide
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+
10
+ # torch-pyodide Python package
11
+
12
+ Package distribution for the browser-first `torch` API running on Pyodide + WebGPU.
13
+
@@ -0,0 +1,4 @@
1
+ # torch-pyodide Python package
2
+
3
+ Package distribution for the browser-first `torch` API running on Pyodide + WebGPU.
4
+
@@ -0,0 +1,17 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "torch-pyodide"
7
+ version = "0.0.1"
8
+ description = "Minimal torch-like API for Pyodide using WebGPU runtime bridge."
9
+ requires-python = ">=3.11"
10
+ readme = "README.md"
11
+
12
+ [project.urls]
13
+ Homepage = "https://github.com/celsowm/torch-pyodide"
14
+ Repository = "https://github.com/celsowm/torch-pyodide"
15
+
16
+ [tool.setuptools]
17
+ packages = ["torch"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,14 @@
1
+ from torch._tensor import _flatten, _infer_shape, _normalize_shape
2
+
3
+
4
+ def test_infer_shape_rectangular():
5
+ assert _infer_shape([[1, 2], [3, 4]]) == [2, 2]
6
+
7
+
8
+ def test_flatten_nested():
9
+ assert _flatten([[1, 2], [3, 4]]) == [1.0, 2.0, 3.0, 4.0]
10
+
11
+
12
+ def test_normalize_shape_sequence():
13
+ assert _normalize_shape((2, 3)) == [2, 3]
14
+
@@ -0,0 +1,49 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Sequence
4
+
5
+ from ._runtime import _get_runtime, _run_js_awaitable
6
+ from ._tensor import Tensor, ones_from_shape, tensor_from_data, zeros_from_shape
7
+
8
+ __all__ = ["Tensor", "init", "tensor", "zeros", "ones", "add", "sub", "mul", "div", "matmul", "relu"]
9
+
10
+
11
+ def init() -> None:
12
+ runtime = _get_runtime()
13
+ _run_js_awaitable(runtime.init())
14
+
15
+
16
+ def tensor(data: object, dtype: str = "float32") -> Tensor:
17
+ return tensor_from_data(data, dtype=dtype)
18
+
19
+
20
+ def zeros(shape: int | Sequence[int], dtype: str = "float32") -> Tensor:
21
+ return zeros_from_shape(shape, dtype=dtype)
22
+
23
+
24
+ def ones(shape: int | Sequence[int], dtype: str = "float32") -> Tensor:
25
+ return ones_from_shape(shape, dtype=dtype)
26
+
27
+
28
+ def add(a: Tensor, b: Tensor) -> Tensor:
29
+ return a.add(b)
30
+
31
+
32
+ def sub(a: Tensor, b: Tensor) -> Tensor:
33
+ return a.sub(b)
34
+
35
+
36
+ def mul(a: Tensor, b: Tensor) -> Tensor:
37
+ return a.mul(b)
38
+
39
+
40
+ def div(a: Tensor, b: Tensor) -> Tensor:
41
+ return a.div(b)
42
+
43
+
44
+ def matmul(a: Tensor, b: Tensor) -> Tensor:
45
+ return a.matmul(b)
46
+
47
+
48
+ def relu(x: Tensor) -> Tensor:
49
+ return x.relu()
@@ -0,0 +1,40 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+
6
+ RUNTIME_GLOBAL_KEY = "__torch_pyodide_runtime__"
7
+
8
+
9
+ def _to_python(value: Any) -> Any:
10
+ if hasattr(value, "to_py"):
11
+ return value.to_py()
12
+ return value
13
+
14
+
15
+ def _get_runtime() -> Any:
16
+ try:
17
+ from js import globalThis # type: ignore
18
+ except Exception as exc: # pragma: no cover - browser specific
19
+ raise RuntimeError("This package only runs inside Pyodide.") from exc
20
+
21
+ runtime = getattr(globalThis, RUNTIME_GLOBAL_KEY, None)
22
+ if runtime is None:
23
+ raise RuntimeError(
24
+ "Torch runtime bridge not found. Install runtime JS and set "
25
+ "globalThis.__torch_pyodide_runtime__ before importing torch."
26
+ )
27
+ return runtime
28
+
29
+
30
+ def _run_js_awaitable(awaitable: Any) -> Any:
31
+ from pyodide.ffi import can_run_sync, run_sync # type: ignore
32
+
33
+ if not can_run_sync():
34
+ raise RuntimeError(
35
+ "Cannot use synchronous torch API: run_sync is unavailable. "
36
+ "Execute Python through pyodide.runPythonAsync(...) and call "
37
+ "sync Python functions from JS via callPromising(...)."
38
+ )
39
+ return _to_python(run_sync(awaitable))
40
+
@@ -0,0 +1,158 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Sequence
5
+
6
+ from ._runtime import _get_runtime, _run_js_awaitable
7
+
8
+
9
+ def _js_meta_to_tuple(meta: object) -> tuple[int, list[int], str]:
10
+ tensor_id = int(getattr(meta, "id"))
11
+ shape_raw = getattr(meta, "shape")
12
+ shape = list(shape_raw.to_py() if hasattr(shape_raw, "to_py") else shape_raw)
13
+ dtype = str(getattr(meta, "dtype"))
14
+ return tensor_id, shape, dtype
15
+
16
+
17
+ @dataclass(slots=True)
18
+ class Tensor:
19
+ _id: int
20
+ _shape: list[int]
21
+ _dtype: str
22
+
23
+ @property
24
+ def shape(self) -> tuple[int, ...]:
25
+ return tuple(self._shape)
26
+
27
+ @property
28
+ def dtype(self) -> str:
29
+ return self._dtype
30
+
31
+ def add(self, other: "Tensor") -> "Tensor":
32
+ runtime = _get_runtime()
33
+ meta = _run_js_awaitable(runtime.add(self._id, other._id))
34
+ tensor_id, shape, dtype = _js_meta_to_tuple(meta)
35
+ return Tensor(tensor_id, shape, dtype)
36
+
37
+ def mul(self, other: "Tensor") -> "Tensor":
38
+ runtime = _get_runtime()
39
+ meta = _run_js_awaitable(runtime.mul(self._id, other._id))
40
+ tensor_id, shape, dtype = _js_meta_to_tuple(meta)
41
+ return Tensor(tensor_id, shape, dtype)
42
+
43
+ def sub(self, other: "Tensor") -> "Tensor":
44
+ runtime = _get_runtime()
45
+ meta = _run_js_awaitable(runtime.sub(self._id, other._id))
46
+ tensor_id, shape, dtype = _js_meta_to_tuple(meta)
47
+ return Tensor(tensor_id, shape, dtype)
48
+
49
+ def div(self, other: "Tensor") -> "Tensor":
50
+ runtime = _get_runtime()
51
+ meta = _run_js_awaitable(runtime.div(self._id, other._id))
52
+ tensor_id, shape, dtype = _js_meta_to_tuple(meta)
53
+ return Tensor(tensor_id, shape, dtype)
54
+
55
+ def matmul(self, other: "Tensor") -> "Tensor":
56
+ runtime = _get_runtime()
57
+ meta = _run_js_awaitable(runtime.matmul(self._id, other._id))
58
+ tensor_id, shape, dtype = _js_meta_to_tuple(meta)
59
+ return Tensor(tensor_id, shape, dtype)
60
+
61
+ def sum(self) -> "Tensor":
62
+ runtime = _get_runtime()
63
+ meta = _run_js_awaitable(runtime.sum(self._id))
64
+ tensor_id, shape, dtype = _js_meta_to_tuple(meta)
65
+ return Tensor(tensor_id, shape, dtype)
66
+
67
+ def mean(self) -> "Tensor":
68
+ runtime = _get_runtime()
69
+ meta = _run_js_awaitable(runtime.mean(self._id))
70
+ tensor_id, shape, dtype = _js_meta_to_tuple(meta)
71
+ return Tensor(tensor_id, shape, dtype)
72
+
73
+ def relu(self) -> "Tensor":
74
+ runtime = _get_runtime()
75
+ meta = _run_js_awaitable(runtime.relu(self._id))
76
+ tensor_id, shape, dtype = _js_meta_to_tuple(meta)
77
+ return Tensor(tensor_id, shape, dtype)
78
+
79
+ def reshape(self, shape: int | Sequence[int]) -> "Tensor":
80
+ runtime = _get_runtime()
81
+ normalized = _normalize_shape(shape)
82
+ meta = _run_js_awaitable(runtime.reshape(self._id, normalized))
83
+ tensor_id, out_shape, out_dtype = _js_meta_to_tuple(meta)
84
+ return Tensor(tensor_id, out_shape, out_dtype)
85
+
86
+ @property
87
+ def T(self) -> "Tensor":
88
+ runtime = _get_runtime()
89
+ meta = _run_js_awaitable(runtime.transpose2d(self._id))
90
+ tensor_id, out_shape, out_dtype = _js_meta_to_tuple(meta)
91
+ return Tensor(tensor_id, out_shape, out_dtype)
92
+
93
+ def to_list(self) -> list[float]:
94
+ runtime = _get_runtime()
95
+ result = _run_js_awaitable(runtime.toList(self._id))
96
+ return list(result)
97
+
98
+ def destroy(self) -> None:
99
+ runtime = _get_runtime()
100
+ _run_js_awaitable(runtime.destroy(self._id))
101
+
102
+
103
+ def _infer_shape(data: object) -> list[int]:
104
+ if not isinstance(data, list):
105
+ return []
106
+ if len(data) == 0:
107
+ return [0]
108
+ first_shape = _infer_shape(data[0])
109
+ for item in data[1:]:
110
+ if _infer_shape(item) != first_shape:
111
+ raise ValueError("tensor() expects a rectangular nested list.")
112
+ return [len(data), *first_shape]
113
+
114
+
115
+ def _flatten(data: object) -> list[float]:
116
+ if isinstance(data, list):
117
+ out: list[float] = []
118
+ for item in data:
119
+ out.extend(_flatten(item))
120
+ return out
121
+ return [float(data)] # type: ignore[arg-type]
122
+
123
+
124
+ def _normalize_shape(shape: int | Sequence[int]) -> list[int]:
125
+ if isinstance(shape, int):
126
+ if shape < 0:
127
+ raise ValueError("shape values must be >= 0")
128
+ return [shape]
129
+
130
+ normalized = [int(v) for v in shape]
131
+ if any(v < 0 for v in normalized):
132
+ raise ValueError("shape values must be >= 0")
133
+ return normalized
134
+
135
+
136
+ def tensor_from_data(data: object, dtype: str = "float32") -> Tensor:
137
+ runtime = _get_runtime()
138
+ shape = _infer_shape(data)
139
+ flat = _flatten(data)
140
+ meta = _run_js_awaitable(runtime.tensorFromData(flat, shape, dtype))
141
+ tensor_id, shape, dtype = _js_meta_to_tuple(meta)
142
+ return Tensor(tensor_id, shape, dtype)
143
+
144
+
145
+ def zeros_from_shape(shape: int | Sequence[int], dtype: str = "float32") -> Tensor:
146
+ runtime = _get_runtime()
147
+ normalized = _normalize_shape(shape)
148
+ meta = _run_js_awaitable(runtime.zeros(normalized, dtype))
149
+ tensor_id, out_shape, out_dtype = _js_meta_to_tuple(meta)
150
+ return Tensor(tensor_id, out_shape, out_dtype)
151
+
152
+
153
+ def ones_from_shape(shape: int | Sequence[int], dtype: str = "float32") -> Tensor:
154
+ runtime = _get_runtime()
155
+ normalized = _normalize_shape(shape)
156
+ meta = _run_js_awaitable(runtime.ones(normalized, dtype))
157
+ tensor_id, out_shape, out_dtype = _js_meta_to_tuple(meta)
158
+ return Tensor(tensor_id, out_shape, out_dtype)
@@ -0,0 +1,13 @@
1
+ Metadata-Version: 2.4
2
+ Name: torch-pyodide
3
+ Version: 0.0.1
4
+ Summary: Minimal torch-like API for Pyodide using WebGPU runtime bridge.
5
+ Project-URL: Homepage, https://github.com/celsowm/torch-pyodide
6
+ Project-URL: Repository, https://github.com/celsowm/torch-pyodide
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+
10
+ # torch-pyodide Python package
11
+
12
+ Package distribution for the browser-first `torch` API running on Pyodide + WebGPU.
13
+
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ tests/test_tensor_utils.py
4
+ torch/__init__.py
5
+ torch/_runtime.py
6
+ torch/_tensor.py
7
+ torch_pyodide.egg-info/PKG-INFO
8
+ torch_pyodide.egg-info/SOURCES.txt
9
+ torch_pyodide.egg-info/dependency_links.txt
10
+ torch_pyodide.egg-info/top_level.txt