haliax 1.4.dev399__tar.gz → 1.4.dev402__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.
- {haliax-1.4.dev399 → haliax-1.4.dev402}/PKG-INFO +1 -1
- haliax-1.4.dev402/src/haliax/__about__.py +1 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/__init__.py +2 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/core.py +3 -8
- haliax-1.4.dev402/src/haliax/field.py +38 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/jax_utils.py +15 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/ops.py +2 -2
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/partitioning.py +57 -29
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/wrap.py +3 -3
- haliax-1.4.dev402/tests/test_field.py +19 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_ops.py +11 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_partitioning.py +20 -1
- haliax-1.4.dev399/src/haliax/__about__.py +0 -1
- {haliax-1.4.dev399 → haliax-1.4.dev402}/.coveragerc +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/.flake8 +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/.github/workflows/publish_dev.yaml +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/.github/workflows/run_pre_commit.yaml +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/.github/workflows/run_quick_levanter_tests.yaml +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/.github/workflows/run_tests.yaml +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/.gitignore +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/.playbooks/add-types.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/.playbooks/wrap-non-named.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/.pre-commit-config.yaml +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/.readthedocs.yaml +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/AGENTS.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/CONTRIBUTING.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/LICENSE +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/README.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/api.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/broadcasting.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/cheatsheet.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/css/material.css +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/css/mkdocstrings.css +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/faq.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/figures/data_parallel_mesh.png +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/figures/data_parallel_mesh_replicated.png +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/figures/device_mesh_1d.png +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/figures/device_mesh_1d_zero.png +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/figures/device_mesh_2d.png +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/figures/device_mesh_2d_batch_partitioned.png +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/figures/device_mesh_2d_data_replicated.png +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/figures/device_mesh_2d_data_replicated_mlp_partitioned.png +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/figures/device_mesh_2d_intermediate_fully_partitioned.png +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/figures/device_mesh_2d_zero.png +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/fp8.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/index.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/indexing.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/matmul.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/nn.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/partitioning.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/primer.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/rearrange.ipynb +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/rearrange.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/requirements.txt +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/scan.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/state-dict.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/tutorial.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/typing.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/docs/vmap.md +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/mkdocs.yml +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/pyproject.toml +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/_src/__init__.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/_src/compile_utils.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/_src/dot.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/_src/einsum.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/_src/fp8.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/_src/parsing.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/_src/rearrange.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/_src/scan.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/_src/state_dict.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/_src/util.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/axis.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/debug.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/haxtyping.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/hof.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/nn/__init__.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/nn/activations.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/nn/attention.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/nn/conv.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/nn/dropout.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/nn/embedding.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/nn/linear.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/nn/loss.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/nn/mlp.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/nn/normalization.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/nn/pool.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/nn/scan.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/quantization.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/random.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/specialized_fns.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/state_dict.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/tree_util.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/types.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/src/haliax/util.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/core_test.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_attention.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_axis.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_conv.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_debug.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_dot.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_dtype_typing.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_einsum.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_fp8.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_hof.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_int8.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_moe_linear.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_namedarray_typing.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_nn.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_parsing.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_pool.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_random.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_rearrange.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_scan.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_scatter_gather.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_specialized_fns.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_state_dict.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_tree_util.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_utils.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/tests/test_visualize_sharding.py +0 -0
- {haliax-1.4.dev399 → haliax-1.4.dev402}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: haliax
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.dev402
|
|
4
4
|
Summary: Named Tensors for Legible Deep Learning in JAX
|
|
5
5
|
Project-URL: Homepage, https://github.com/stanford-crfm/haliax
|
|
6
6
|
Project-URL: Bug Tracker, https://github.com/stanford-crfm/haliax/issues/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.4.dev402"
|
|
@@ -17,6 +17,7 @@ import haliax.random as random
|
|
|
17
17
|
import haliax.state_dict as state_dict
|
|
18
18
|
import haliax.tree_util as tree_util
|
|
19
19
|
import haliax.util as util
|
|
20
|
+
from .field import field
|
|
20
21
|
|
|
21
22
|
from ._src.dot import dot
|
|
22
23
|
from ._src.einsum import einsum
|
|
@@ -932,6 +933,7 @@ __all__ = [
|
|
|
932
933
|
"tree_util",
|
|
933
934
|
"nn",
|
|
934
935
|
"state_dict",
|
|
936
|
+
"field",
|
|
935
937
|
"Axis",
|
|
936
938
|
"AxisSpec",
|
|
937
939
|
"AxisSelection",
|
|
@@ -15,7 +15,7 @@ import numpy as np
|
|
|
15
15
|
|
|
16
16
|
import haliax
|
|
17
17
|
import haliax.axis
|
|
18
|
-
from haliax.jax_utils import is_jax_array_like, is_pallas_dslice
|
|
18
|
+
from haliax.jax_utils import ensure_scalar, is_jax_array_like, is_pallas_dslice
|
|
19
19
|
from haliax.util import ensure_tuple
|
|
20
20
|
|
|
21
21
|
from ._src.util import index_where, py_slice, slice_t
|
|
@@ -1115,9 +1115,7 @@ def updated_slice(
|
|
|
1115
1115
|
if axis_index is None:
|
|
1116
1116
|
raise ValueError(f"axis {axis} not found in {array}")
|
|
1117
1117
|
if isinstance(s, NamedArray): # this can happen in the vmap case
|
|
1118
|
-
|
|
1119
|
-
raise ValueError(f"NamedArray {s} must be a scalar for axis {axis} in updated_slice")
|
|
1120
|
-
s = s.scalar()
|
|
1118
|
+
s = ensure_scalar(s, name=str(axis))
|
|
1121
1119
|
|
|
1122
1120
|
array_slice_indices[axis_index] = s
|
|
1123
1121
|
total_length = array.axes[axis_index].size
|
|
@@ -1376,10 +1374,7 @@ def roll(
|
|
|
1376
1374
|
if axis_indices is None:
|
|
1377
1375
|
raise ValueError(f"axis {axis} not found in {array}")
|
|
1378
1376
|
|
|
1379
|
-
|
|
1380
|
-
if shift.ndim != 0:
|
|
1381
|
-
raise TypeError("shift must be a scalar NamedArray")
|
|
1382
|
-
shift = shift.array
|
|
1377
|
+
shift = ensure_scalar(shift, name="shift")
|
|
1383
1378
|
|
|
1384
1379
|
return NamedArray(jnp.roll(array.array, shift, axis_indices), array.axes)
|
|
1385
1380
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Callable
|
|
4
|
+
|
|
5
|
+
import equinox as eqx
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def field(
|
|
9
|
+
*,
|
|
10
|
+
converter: Callable[[Any], Any] | None = None,
|
|
11
|
+
static: bool = False,
|
|
12
|
+
axis_names: tuple[str, ...] | None = None,
|
|
13
|
+
**kwargs,
|
|
14
|
+
):
|
|
15
|
+
"""Wrapper around :func:`equinox.field` with optional ``axis_names`` metadata.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
converter: Optional function applied to the value during dataclass initialisation.
|
|
19
|
+
static: Whether the field is static in the PyTree.
|
|
20
|
+
axis_names: Optional axis names associated with array fields. Cannot be
|
|
21
|
+
specified together with ``static=True``.
|
|
22
|
+
**kwargs: Additional keyword arguments forwarded to :func:`dataclasses.field`.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
A dataclasses field configured like :func:`equinox.field` with additional
|
|
26
|
+
``axis_names`` metadata.
|
|
27
|
+
"""
|
|
28
|
+
if static and axis_names is not None:
|
|
29
|
+
raise ValueError("axis_names cannot be specified together with static=True")
|
|
30
|
+
|
|
31
|
+
metadata = dict(kwargs.pop("metadata", {}))
|
|
32
|
+
metadata["axis_names"] = axis_names
|
|
33
|
+
|
|
34
|
+
field_kwargs = {}
|
|
35
|
+
if converter is not None:
|
|
36
|
+
field_kwargs["converter"] = converter
|
|
37
|
+
|
|
38
|
+
return eqx.field(static=static, metadata=metadata, **field_kwargs, **kwargs)
|
|
@@ -153,6 +153,21 @@ def is_scalarish(x):
|
|
|
153
153
|
return jnp.isscalar(x) or (getattr(x, "shape", None) == ())
|
|
154
154
|
|
|
155
155
|
|
|
156
|
+
def ensure_scalar(x, *, name: str = "value"):
|
|
157
|
+
"""Return ``x`` if it is not a :class:`NamedArray`, otherwise ensure it is a scalar.
|
|
158
|
+
|
|
159
|
+
This is useful for APIs that can accept either Python scalars or scalar
|
|
160
|
+
``NamedArray`` objects (for example ``roll`` or ``updated_slice``). If ``x``
|
|
161
|
+
is a ``NamedArray`` with rank greater than 0 a :class:`TypeError` is raised.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
if isinstance(x, haliax.NamedArray):
|
|
165
|
+
if x.ndim != 0:
|
|
166
|
+
raise TypeError(f"{name} must be a scalar NamedArray")
|
|
167
|
+
return x.array
|
|
168
|
+
return x
|
|
169
|
+
|
|
170
|
+
|
|
156
171
|
def is_on_mac_metal():
|
|
157
172
|
return jax.devices()[0].platform.lower() == "metal"
|
|
158
173
|
|
|
@@ -9,7 +9,7 @@ import haliax
|
|
|
9
9
|
|
|
10
10
|
from .axis import Axis, AxisSelector, axis_name
|
|
11
11
|
from .core import NamedArray, NamedOrNumeric, broadcast_arrays, broadcast_arrays_and_return_axes, named
|
|
12
|
-
from .jax_utils import is_scalarish
|
|
12
|
+
from .jax_utils import ensure_scalar, is_scalarish
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def trace(array: NamedArray, axis1: AxisSelector, axis2: AxisSelector, offset=0, dtype=None) -> NamedArray:
|
|
@@ -89,7 +89,7 @@ def where(
|
|
|
89
89
|
x = named(x, ())
|
|
90
90
|
x, y = broadcast_arrays(x, y)
|
|
91
91
|
if isinstance(condition, NamedArray):
|
|
92
|
-
condition = condition
|
|
92
|
+
condition = ensure_scalar(condition, name="condition")
|
|
93
93
|
return jax.lax.cond(condition, lambda _: x, lambda _: y, None)
|
|
94
94
|
|
|
95
95
|
condition, x, y = broadcast_arrays(condition, x, y) # type: ignore
|
|
@@ -177,24 +177,21 @@ def shard_with_axis_mapping(x: T, mapping: ResourceMapping, mesh: Optional[Mesh]
|
|
|
177
177
|
return shard(x, mapping, mesh)
|
|
178
178
|
|
|
179
179
|
|
|
180
|
-
def
|
|
180
|
+
def pspec_for(
|
|
181
181
|
tree: PyTree,
|
|
182
182
|
resource_mapping: Optional[ResourceMapping] = None,
|
|
183
183
|
preserve_existing_shardings: bool = True,
|
|
184
184
|
use_auto_sharding: bool = True,
|
|
185
|
-
mesh: Optional[Mesh] = None,
|
|
186
185
|
) -> PyTree:
|
|
187
|
-
"""
|
|
188
|
-
Infer the sharding for a module, to be used with named_jit.
|
|
189
|
-
The basic idea is to tree all NamedArrays as leaves for the purposes of this function,
|
|
190
|
-
and to create NamedShardings from those names plus the resource_mapping.
|
|
191
|
-
If preserve_existing_shardings is True, then NamedArrays that are already sharded are left alone.
|
|
186
|
+
"""Infer the :class:`PartitionSpec` for a module.
|
|
192
187
|
|
|
193
|
-
|
|
188
|
+
This behaves like :func:`infer_resource_partitions` but returns ``PartitionSpec``
|
|
189
|
+
objects instead of :class:`~jax.sharding.NamedSharding`. It is primarily a helper
|
|
190
|
+
for :func:`infer_resource_partitions` but may be useful when only the partition
|
|
191
|
+
specification is required.
|
|
194
192
|
|
|
195
|
-
If
|
|
196
|
-
|
|
197
|
-
don't have a sharding.
|
|
193
|
+
If ``preserve_existing_shardings`` is ``True``, then arrays that already have a
|
|
194
|
+
sharding are left untouched and ``None`` is returned for those leaves.
|
|
198
195
|
"""
|
|
199
196
|
if resource_mapping is None:
|
|
200
197
|
resource_mapping = current_thread_local_mapping()
|
|
@@ -202,35 +199,26 @@ def infer_resource_partitions(
|
|
|
202
199
|
if resource_mapping is None:
|
|
203
200
|
raise ValueError("No resource mapping found")
|
|
204
201
|
|
|
205
|
-
mesh = mesh or _get_mesh()
|
|
206
|
-
assert not isinstance(mesh, dict)
|
|
207
|
-
|
|
208
202
|
def partition_spec(node: typing.Any):
|
|
209
203
|
if isinstance(node, NamedArray):
|
|
210
204
|
# If our NamedArray doesn't have an array (or a shapedtypestruct), we can't shard it
|
|
211
|
-
# so better to not try
|
|
212
205
|
if not is_jax_array_like(node.array):
|
|
213
206
|
return None
|
|
214
207
|
|
|
215
|
-
if preserve_existing_shardings
|
|
216
|
-
current_sharding = getattr(node.array, "sharding", None)
|
|
217
|
-
else:
|
|
218
|
-
current_sharding = None
|
|
219
|
-
|
|
208
|
+
current_sharding = getattr(node.array, "sharding", None) if preserve_existing_shardings else None
|
|
220
209
|
if current_sharding is not None:
|
|
221
|
-
return
|
|
210
|
+
return None
|
|
222
211
|
else:
|
|
223
|
-
|
|
224
|
-
return sharding
|
|
212
|
+
return pspec_for_axis(node.axes, resource_mapping)
|
|
225
213
|
elif is_jax_array_like(node):
|
|
226
214
|
sharding = getattr(node, "sharding", None)
|
|
227
215
|
# TODO: these are usually replicated. Is there a better way to tell?
|
|
228
216
|
if node.shape == ():
|
|
229
|
-
return
|
|
217
|
+
return PartitionSpec()
|
|
230
218
|
elif isinstance(sharding, SingleDeviceSharding):
|
|
231
|
-
return
|
|
232
|
-
elif sharding is not None:
|
|
233
|
-
return
|
|
219
|
+
return PartitionSpec(None)
|
|
220
|
+
elif sharding is not None and preserve_existing_shardings:
|
|
221
|
+
return None
|
|
234
222
|
# elif use_auto_sharding:
|
|
235
223
|
# TODO: auto doesn't seem to really work reliably yet
|
|
236
224
|
# compat between 0.4.10 and 0.4.11
|
|
@@ -238,15 +226,54 @@ def infer_resource_partitions(
|
|
|
238
226
|
# return AUTO(mesh)
|
|
239
227
|
# else:
|
|
240
228
|
# return AUTO
|
|
241
|
-
return
|
|
229
|
+
return PartitionSpec(None)
|
|
242
230
|
elif isinstance(node, (bool, float, complex, int)):
|
|
243
|
-
return
|
|
231
|
+
return PartitionSpec()
|
|
244
232
|
else:
|
|
245
233
|
return None
|
|
246
234
|
|
|
247
235
|
return htu.tree_map(partition_spec, tree)
|
|
248
236
|
|
|
249
237
|
|
|
238
|
+
def infer_resource_partitions(
|
|
239
|
+
tree: PyTree,
|
|
240
|
+
resource_mapping: Optional[ResourceMapping] = None,
|
|
241
|
+
preserve_existing_shardings: bool = True,
|
|
242
|
+
use_auto_sharding: bool = True,
|
|
243
|
+
mesh: Optional[Mesh] = None,
|
|
244
|
+
) -> PyTree:
|
|
245
|
+
"""
|
|
246
|
+
Infer the sharding for a module, to be used with ``named_jit``.
|
|
247
|
+
|
|
248
|
+
This first calls :func:`pspec_for` to compute ``PartitionSpec`` objects and then
|
|
249
|
+
wraps them in :class:`~jax.sharding.NamedSharding` using the provided mesh. If
|
|
250
|
+
``preserve_existing_shardings`` is ``True``, then arrays that are already sharded
|
|
251
|
+
retain their current sharding.
|
|
252
|
+
"""
|
|
253
|
+
pspecs = pspec_for(
|
|
254
|
+
tree,
|
|
255
|
+
resource_mapping=resource_mapping,
|
|
256
|
+
preserve_existing_shardings=preserve_existing_shardings,
|
|
257
|
+
use_auto_sharding=use_auto_sharding,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
mesh = mesh or _get_mesh()
|
|
261
|
+
assert not isinstance(mesh, dict)
|
|
262
|
+
|
|
263
|
+
def to_sharding(node: typing.Any, spec: typing.Any):
|
|
264
|
+
if spec is None:
|
|
265
|
+
if isinstance(node, NamedArray):
|
|
266
|
+
return getattr(node.array, "sharding", None)
|
|
267
|
+
elif is_jax_array_like(node):
|
|
268
|
+
return getattr(node, "sharding", None)
|
|
269
|
+
else:
|
|
270
|
+
return None
|
|
271
|
+
else:
|
|
272
|
+
return NamedSharding(mesh, spec)
|
|
273
|
+
|
|
274
|
+
return htu.tree_map(to_sharding, tree, pspecs)
|
|
275
|
+
|
|
276
|
+
|
|
250
277
|
class WrappedCallable(typing.Protocol[Args, R]):
|
|
251
278
|
"""
|
|
252
279
|
A wrapper for a callable that preserves the original function's name and qualname.
|
|
@@ -659,6 +686,7 @@ __all__ = [
|
|
|
659
686
|
"auto_sharded",
|
|
660
687
|
"shard",
|
|
661
688
|
"shard_with_axis_mapping",
|
|
689
|
+
"pspec_for",
|
|
662
690
|
"infer_resource_partitions",
|
|
663
691
|
"named_jit",
|
|
664
692
|
"fsdp",
|
|
@@ -5,7 +5,7 @@ import jax
|
|
|
5
5
|
from haliax.core import NamedArray, _broadcast_order, broadcast_to
|
|
6
6
|
|
|
7
7
|
from .axis import AxisSelection, AxisSelector, axis_spec_to_shape_dict, eliminate_axes
|
|
8
|
-
from .jax_utils import is_scalarish
|
|
8
|
+
from .jax_utils import ensure_scalar, is_scalarish
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def wrap_elemwise_unary(f, a, *args, **kwargs):
|
|
@@ -105,7 +105,7 @@ def wrap_elemwise_binary(op):
|
|
|
105
105
|
else:
|
|
106
106
|
if is_scalarish(b):
|
|
107
107
|
return NamedArray(op(a.array, b), a.axes)
|
|
108
|
-
a = a
|
|
108
|
+
a = ensure_scalar(a)
|
|
109
109
|
return op(a, b)
|
|
110
110
|
|
|
111
111
|
return NamedArray(op(a.array, b), a.axes)
|
|
@@ -119,7 +119,7 @@ def wrap_elemwise_binary(op):
|
|
|
119
119
|
else:
|
|
120
120
|
if is_scalarish(a):
|
|
121
121
|
return NamedArray(op(a, b.array), b.axes)
|
|
122
|
-
b = b
|
|
122
|
+
b = ensure_scalar(b)
|
|
123
123
|
return op(a, b)
|
|
124
124
|
|
|
125
125
|
return NamedArray(op(a, b.array), b.axes)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import jax.numpy as jnp
|
|
2
|
+
import equinox as eqx
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
import haliax as hax
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class M(eqx.Module):
|
|
9
|
+
a: jnp.ndarray = hax.field(axis_names=("batch",))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_axis_names_metadata():
|
|
13
|
+
field = M.__dataclass_fields__["a"]
|
|
14
|
+
assert field.metadata["axis_names"] == ("batch",)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_axis_names_static_exclusive():
|
|
18
|
+
with pytest.raises(ValueError):
|
|
19
|
+
hax.field(static=True, axis_names=("x",))
|
|
@@ -437,3 +437,14 @@ def test_roll_scalar_named_shift():
|
|
|
437
437
|
|
|
438
438
|
assert rolled.axes == arr.axes
|
|
439
439
|
assert jnp.all(rolled.array == expected)
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def test_roll_bad_named_shift():
|
|
443
|
+
H = Axis("H", 4)
|
|
444
|
+
W = Axis("W", 3)
|
|
445
|
+
|
|
446
|
+
arr = hax.arange((H, W))
|
|
447
|
+
shift = hax.arange((Axis("dummy", 2),))
|
|
448
|
+
|
|
449
|
+
with pytest.raises(TypeError):
|
|
450
|
+
hax.roll(arr, shift, H)
|
|
@@ -7,7 +7,13 @@ from jaxtyping import Array
|
|
|
7
7
|
|
|
8
8
|
import haliax as hax
|
|
9
9
|
from haliax import Axis, NamedArray
|
|
10
|
-
from haliax.partitioning import
|
|
10
|
+
from haliax.partitioning import (
|
|
11
|
+
ResourceAxis,
|
|
12
|
+
axis_mapping,
|
|
13
|
+
infer_resource_partitions,
|
|
14
|
+
named_jit,
|
|
15
|
+
pspec_for,
|
|
16
|
+
)
|
|
11
17
|
from test_utils import skip_if_not_enough_devices
|
|
12
18
|
|
|
13
19
|
|
|
@@ -40,6 +46,19 @@ def test_infer_named_axes():
|
|
|
40
46
|
assert axes.unnamed1.is_fully_replicated
|
|
41
47
|
|
|
42
48
|
|
|
49
|
+
def test_pspec_for_named_axes():
|
|
50
|
+
mesh = Mesh(np.array(jax.devices()).reshape(-1, 1), (ResourceAxis.DATA, ResourceAxis.MODEL))
|
|
51
|
+
with axis_mapping(resource_map), mesh:
|
|
52
|
+
mod = MyModule(named=hax.ones((Dim1, Dim2, Dim3)), unnamed1=jnp.ones(Dim2.size), static_field=1)
|
|
53
|
+
|
|
54
|
+
specs: MyModule = pspec_for(mod, preserve_existing_shardings=False)
|
|
55
|
+
|
|
56
|
+
spec = PartitionSpec(None, ResourceAxis.DATA, ResourceAxis.MODEL)
|
|
57
|
+
|
|
58
|
+
assert specs.named == spec
|
|
59
|
+
assert specs.unnamed1 == PartitionSpec(None)
|
|
60
|
+
|
|
61
|
+
|
|
43
62
|
class MyModuleInit(eqx.Module):
|
|
44
63
|
named: NamedArray
|
|
45
64
|
unnamed1: Array
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.4.dev399"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|