haliax 1.4.dev444__tar.gz → 1.4.dev446__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.dev444 → haliax-1.4.dev446}/PKG-INFO +1 -1
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/__about__.py +1 -1
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/_src/state_dict.py +20 -7
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/jax_utils.py +82 -3
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/nn/scan.py +33 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_scan.py +59 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/.agents/projects/api_parity.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/.coveragerc +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/.flake8 +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/.github/workflows/publish_dev.yaml +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/.github/workflows/run_pre_commit.yaml +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/.github/workflows/run_quick_levanter_tests.yaml +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/.github/workflows/run_tests.yaml +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/.gitignore +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/.playbooks/add-types.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/.playbooks/wrap-non-named.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/.pre-commit-config.yaml +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/.readthedocs.yaml +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/AGENTS.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/AUTHORS.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/CONTRIBUTING.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/CONTRIBUTORS.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/LICENSE +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/README.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/api.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/broadcasting.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/cheatsheet.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/css/material.css +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/css/mkdocstrings.css +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/faq.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/figures/data_parallel_mesh.png +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/figures/data_parallel_mesh_replicated.png +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/figures/device_mesh_1d.png +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/figures/device_mesh_1d_zero.png +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/figures/device_mesh_2d.png +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/figures/device_mesh_2d_batch_partitioned.png +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/figures/device_mesh_2d_data_replicated.png +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/figures/device_mesh_2d_data_replicated_mlp_partitioned.png +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/figures/device_mesh_2d_intermediate_fully_partitioned.png +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/figures/device_mesh_2d_zero.png +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/fp8.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/index.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/indexing.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/matmul.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/nn.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/partitioning.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/primer.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/rearrange.ipynb +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/rearrange.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/requirements.txt +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/scan.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/state-dict.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/tutorial.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/typing.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/docs/vmap.md +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/etc/license_header.txt +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/mkdocs.yml +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/pyproject.toml +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/__init__.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/_src/__init__.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/_src/compile_utils.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/_src/dot.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/_src/einsum.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/_src/fp8.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/_src/parsing.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/_src/rearrange.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/_src/scan.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/_src/util.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/axis.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/core.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/debug.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/fft.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/field.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/haxtyping.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/hof.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/nn/__init__.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/nn/activations.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/nn/attention.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/nn/conv.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/nn/dropout.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/nn/embedding.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/nn/linear.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/nn/loss.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/nn/mlp.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/nn/mup.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/nn/normalization.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/nn/pool.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/ops.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/partitioning.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/poly.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/quantization.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/random.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/specialized_fns.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/state_dict.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/tree.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/tree_util.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/types.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/util.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/src/haliax/wrap.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/core_test.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_attention.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_axis.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_bitwise_ops.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_conv.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_debug.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_dot.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_dtype_typing.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_einsum.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_fft.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_field.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_fp8.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_hof.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_int8.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_moe_linear.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_mup_coordinate_check.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_mup_embedding.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_mup_linear.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_namedarray_typing.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_nan_reductions.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_nn.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_ops.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_parsing.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_partitioning.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_poly_ops.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_pool.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_random.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_rearrange.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_scatter_gather.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_specialized_fns.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_state_dict.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_tree_util.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_utils.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/tests/test_visualize_sharding.py +0 -0
- {haliax-1.4.dev444 → haliax-1.4.dev446}/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.dev446
|
|
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/
|
|
@@ -13,7 +13,7 @@ import jax
|
|
|
13
13
|
import jax.numpy as jnp
|
|
14
14
|
import numpy as np
|
|
15
15
|
from jax import ShapeDtypeStruct
|
|
16
|
-
from
|
|
16
|
+
from haliax.jax_utils import sync_global_devices
|
|
17
17
|
from jax.sharding import Mesh, NamedSharding, PartitionSpec
|
|
18
18
|
from jax.tree_util import DictKey, FlattenedIndexKey, GetAttrKey, SequenceKey
|
|
19
19
|
from jaxtyping import PyTree
|
|
@@ -68,7 +68,11 @@ def flatten_modules_for_export(t: T) -> T:
|
|
|
68
68
|
)
|
|
69
69
|
return module
|
|
70
70
|
|
|
71
|
-
return scan_aware_tree_map(
|
|
71
|
+
return scan_aware_tree_map(
|
|
72
|
+
_flatten_module,
|
|
73
|
+
t,
|
|
74
|
+
is_leaf=lambda x: isinstance(x, ModuleWithStateDictSerialization),
|
|
75
|
+
)
|
|
72
76
|
|
|
73
77
|
|
|
74
78
|
def unflatten_modules_from_export(t: T, template: T) -> T:
|
|
@@ -88,7 +92,10 @@ def unflatten_modules_from_export(t: T, template: T) -> T:
|
|
|
88
92
|
return module
|
|
89
93
|
|
|
90
94
|
return scan_aware_tree_map(
|
|
91
|
-
_unflatten_module,
|
|
95
|
+
_unflatten_module,
|
|
96
|
+
t,
|
|
97
|
+
template,
|
|
98
|
+
is_leaf=lambda x: isinstance(x, ModuleWithStateDictSerialization),
|
|
92
99
|
)
|
|
93
100
|
|
|
94
101
|
|
|
@@ -187,9 +194,13 @@ def from_state_dict(tree: T, state_dict: StateDict, prefix: str | None = None) -
|
|
|
187
194
|
else:
|
|
188
195
|
return default_eqx_module_from_state_dict(tree, state_dict, prefix)
|
|
189
196
|
elif isinstance(tree, list):
|
|
190
|
-
return [
|
|
197
|
+
return [
|
|
198
|
+
from_state_dict(item, state_dict, with_prefix(prefix, str(i))) for i, item in enumerate(tree)
|
|
199
|
+
] # type: ignore
|
|
191
200
|
elif isinstance(tree, dict):
|
|
192
|
-
return {
|
|
201
|
+
return {
|
|
202
|
+
k: from_state_dict(v, state_dict, prefix=with_prefix(prefix, k)) for k, v in tree.items()
|
|
203
|
+
} # type: ignore
|
|
193
204
|
elif isinstance(tree, NamedArray):
|
|
194
205
|
if prefix is None:
|
|
195
206
|
raise ValueError("Cannot extract a leaf value from a torch dict without a prefix")
|
|
@@ -383,7 +394,8 @@ def to_numpy_state_dict(model, prefix: str | None = None) -> StateDict:
|
|
|
383
394
|
# TODO: ensure that this mesh arranges devices correctly
|
|
384
395
|
# (jax seems to do this internally itself, so we should be fine?)
|
|
385
396
|
process_mesh = Mesh(
|
|
386
|
-
np.array(jax.devices()).reshape((jax.process_count(), -1)),
|
|
397
|
+
np.array(jax.devices()).reshape((jax.process_count(), -1)),
|
|
398
|
+
("process", "device"),
|
|
387
399
|
)
|
|
388
400
|
|
|
389
401
|
# now we need to find an axis along which we can shard the array.
|
|
@@ -391,7 +403,8 @@ def to_numpy_state_dict(model, prefix: str | None = None) -> StateDict:
|
|
|
391
403
|
|
|
392
404
|
try:
|
|
393
405
|
axis_to_shard = index_where(
|
|
394
|
-
lambda axis_size: axis_size % process_mesh.devices.size == 0,
|
|
406
|
+
lambda axis_size: axis_size % process_mesh.devices.size == 0,
|
|
407
|
+
arr.shape,
|
|
395
408
|
)
|
|
396
409
|
except ValueError:
|
|
397
410
|
return np.array(arr)
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import functools as ft
|
|
7
7
|
import typing
|
|
8
8
|
import warnings
|
|
9
|
+
import zlib
|
|
9
10
|
from typing import Any, Callable, Sequence
|
|
10
11
|
|
|
11
12
|
import equinox as eqx
|
|
@@ -14,6 +15,8 @@ import numpy as np
|
|
|
14
15
|
from jax import Array
|
|
15
16
|
from jax import numpy as jnp
|
|
16
17
|
from jax import random as jrandom
|
|
18
|
+
from jax.experimental.multihost_utils import host_local_array_to_global_array
|
|
19
|
+
from jax.sharding import PartitionSpec
|
|
17
20
|
from jax.ad_checkpoint import checkpoint_name
|
|
18
21
|
from jax.typing import DTypeLike
|
|
19
22
|
from jaxtyping import PRNGKeyArray
|
|
@@ -39,6 +42,71 @@ class Static(eqx.Module):
|
|
|
39
42
|
value: Any = eqx.field(static=True)
|
|
40
43
|
|
|
41
44
|
|
|
45
|
+
# Non-busted version of broadcast_one_to_all from jax.multihost_utils. (The issue is that if you use a non-contiguous
|
|
46
|
+
# mesh, their utility blows up because it makes a contiguous mesh.)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _psum(xs: Any) -> Any:
|
|
50
|
+
return jax.tree.map(lambda x: jnp.sum(x, dtype=x.dtype, axis=0), xs)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def broadcast_one_to_all(in_tree: Any, is_source: bool | None = None) -> Any:
|
|
54
|
+
"""Broadcast data from a source host (host 0 by default) to all other hosts.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
in_tree: pytree of arrays - each array *must* have the same shape across the
|
|
58
|
+
hosts.
|
|
59
|
+
is_source: optional bool denoting whether the caller is the source. Only
|
|
60
|
+
'source host' will contribute the data for the broadcast. If None, then
|
|
61
|
+
host 0 is used.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
A pytree matching in_tree where the leaves now all contain the data from the
|
|
65
|
+
first host.
|
|
66
|
+
"""
|
|
67
|
+
if jax.process_count() == 1:
|
|
68
|
+
return jax.tree.map(np.asarray, in_tree)
|
|
69
|
+
|
|
70
|
+
if is_source is None:
|
|
71
|
+
is_source = jax.process_index() == 0
|
|
72
|
+
|
|
73
|
+
devices: np.ndarray = np.array(jax.devices()).reshape(jax.process_count(), jax.local_device_count())
|
|
74
|
+
global_mesh = jax.sharding.Mesh(devices, ("processes", "local_devices"))
|
|
75
|
+
pspec = PartitionSpec("processes")
|
|
76
|
+
|
|
77
|
+
def pre_jit(x):
|
|
78
|
+
if is_source:
|
|
79
|
+
inp = x
|
|
80
|
+
else:
|
|
81
|
+
inp = np.zeros_like(x)
|
|
82
|
+
inp = np.expand_dims(inp, axis=0)
|
|
83
|
+
return host_local_array_to_global_array(inp, global_mesh, pspec)
|
|
84
|
+
|
|
85
|
+
def post_jit(x):
|
|
86
|
+
return jax.device_get(x.addressable_data(0))
|
|
87
|
+
|
|
88
|
+
with haliax.partitioning.set_mesh(global_mesh):
|
|
89
|
+
in_tree = jax.tree.map(pre_jit, in_tree)
|
|
90
|
+
out_tree = jax.jit(
|
|
91
|
+
_psum,
|
|
92
|
+
out_shardings=jax.sharding.NamedSharding(global_mesh, PartitionSpec()),
|
|
93
|
+
)(in_tree)
|
|
94
|
+
return jax.tree.map(post_jit, out_tree)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def assert_equal(in_tree, fail_message: str = ""):
|
|
98
|
+
"""Verifies that all the hosts have the same tree of values."""
|
|
99
|
+
expected = broadcast_one_to_all(in_tree)
|
|
100
|
+
if not jax.tree_util.tree_all(jax.tree_util.tree_map(lambda *x: np.all(np.equal(*x)), in_tree, expected)):
|
|
101
|
+
raise AssertionError(f"{fail_message} Expected: {expected}; got: {in_tree}.")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def sync_global_devices(name: str):
|
|
105
|
+
"""Creates a barrier across all hosts/devices."""
|
|
106
|
+
h = np.uint32(zlib.crc32(name.encode()))
|
|
107
|
+
assert_equal(h, f"sync_global_devices name mismatch ('{name}')")
|
|
108
|
+
|
|
109
|
+
|
|
42
110
|
def shaped_rng_split(key, split_shape: int | Sequence[int] = 2) -> PRNGKeyArray:
|
|
43
111
|
if isinstance(split_shape, int):
|
|
44
112
|
num_splits = split_shape
|
|
@@ -69,14 +137,25 @@ def maybe_rng_split(key: PRNGKeyArray | None, num: int = 2):
|
|
|
69
137
|
def filter_eval_shape(*args, **kwargs):
|
|
70
138
|
import warnings
|
|
71
139
|
|
|
72
|
-
warnings.warn(
|
|
140
|
+
warnings.warn(
|
|
141
|
+
"filter_eval_shape is deprecated, use eqx.filter_eval_shape instead",
|
|
142
|
+
DeprecationWarning,
|
|
143
|
+
)
|
|
73
144
|
return eqx.filter_eval_shape(*args, **kwargs)
|
|
74
145
|
|
|
75
146
|
|
|
76
|
-
def filter_checkpoint(
|
|
147
|
+
def filter_checkpoint(
|
|
148
|
+
fun: Callable,
|
|
149
|
+
*,
|
|
150
|
+
prevent_cse: bool = True,
|
|
151
|
+
policy: Callable[..., bool] | None = None,
|
|
152
|
+
):
|
|
77
153
|
"""As `jax.checkpoint`, but allows any Python object as inputs and outputs"""
|
|
78
154
|
|
|
79
|
-
warnings.warn(
|
|
155
|
+
warnings.warn(
|
|
156
|
+
"filter_checkpoint is deprecated, use eqx.filter_checkpoint instead",
|
|
157
|
+
DeprecationWarning,
|
|
158
|
+
)
|
|
80
159
|
|
|
81
160
|
return eqx.filter_checkpoint(fun, prevent_cse=prevent_cse, policy=policy)
|
|
82
161
|
|
|
@@ -150,6 +150,11 @@ class BlockFoldable(Protocol[M]):
|
|
|
150
150
|
"""
|
|
151
151
|
...
|
|
152
152
|
|
|
153
|
+
def get_layer(self, index: int) -> M:
|
|
154
|
+
"""Return the ``index``th layer of the folded module."""
|
|
155
|
+
|
|
156
|
+
...
|
|
157
|
+
|
|
153
158
|
|
|
154
159
|
class BlockSeq(ModuleWithStateDictSerialization, Generic[M]):
|
|
155
160
|
"""
|
|
@@ -377,6 +382,11 @@ class BlockSeq(ModuleWithStateDictSerialization, Generic[M]):
|
|
|
377
382
|
|
|
378
383
|
return state_dict
|
|
379
384
|
|
|
385
|
+
def get_layer(self, index: int) -> M:
|
|
386
|
+
"""Return the ``index``th block in this sequential container."""
|
|
387
|
+
|
|
388
|
+
return self.blocks[index]
|
|
389
|
+
|
|
380
390
|
@property
|
|
381
391
|
def _output_ckpt_name(self):
|
|
382
392
|
return f"BlockSeq[{self.Block}, {self.blocks[0].__class__.__name__}].outputs"
|
|
@@ -442,6 +452,12 @@ class Stacked(ModuleWithStateDictSerialization, Generic[M]):
|
|
|
442
452
|
Block: Axis = eqx.field(static=True)
|
|
443
453
|
gradient_checkpointing: ScanCheckpointPolicy = eqx.field(static=True)
|
|
444
454
|
|
|
455
|
+
@property
|
|
456
|
+
def Layers(self) -> Axis:
|
|
457
|
+
"""Alias for :attr:`Block` used by some downstream code."""
|
|
458
|
+
|
|
459
|
+
return self.Block
|
|
460
|
+
|
|
445
461
|
@classmethod
|
|
446
462
|
def init(
|
|
447
463
|
cls,
|
|
@@ -700,6 +716,23 @@ class Stacked(ModuleWithStateDictSerialization, Generic[M]):
|
|
|
700
716
|
unstacked_leaves = tuple(zip(*unstacked_leaves))
|
|
701
717
|
return tuple(map(lambda x: jax.tree_util.tree_unflatten(structure, x), unstacked_leaves))
|
|
702
718
|
|
|
719
|
+
def get_layer(self, index: int) -> M:
|
|
720
|
+
"""Return the ``index``th layer of this stacked module."""
|
|
721
|
+
|
|
722
|
+
def select_leaf(leaf):
|
|
723
|
+
if isinstance(leaf, haliax.NamedArray):
|
|
724
|
+
if haliax.selects_axis(leaf.axes, self.Block):
|
|
725
|
+
return leaf[self.Block, index]
|
|
726
|
+
else:
|
|
727
|
+
return leaf
|
|
728
|
+
if is_jax_or_hax_array_like(leaf):
|
|
729
|
+
if getattr(leaf, "shape", ()) and leaf.shape[0] == self.Block.size:
|
|
730
|
+
return leaf[index]
|
|
731
|
+
return leaf
|
|
732
|
+
return leaf
|
|
733
|
+
|
|
734
|
+
return haliax.tree_util.tree_map(select_leaf, self.stacked)
|
|
735
|
+
|
|
703
736
|
def to_state_dict(self, prefix: str | None = None) -> StateDict:
|
|
704
737
|
# this method needs to "devectorize" the blocks, so that we have a list of blocks h.0.FOO, h.1.FOO, etc.
|
|
705
738
|
# first just do the normal thing with our own dict, which we'll post-process
|
|
@@ -51,6 +51,65 @@ def test_unstacked():
|
|
|
51
51
|
assert hax.all(module.array == m.stacked.array[i])
|
|
52
52
|
|
|
53
53
|
|
|
54
|
+
def test_get_layer_stacked():
|
|
55
|
+
class Module(eqx.Module):
|
|
56
|
+
named: hax.NamedArray
|
|
57
|
+
array: jax.Array
|
|
58
|
+
static: int = eqx.static_field()
|
|
59
|
+
|
|
60
|
+
def __call__(self, x, *, key): # pragma: no cover - unused in this test
|
|
61
|
+
return x + self.array + self.static
|
|
62
|
+
|
|
63
|
+
@staticmethod
|
|
64
|
+
def init(named, array, static):
|
|
65
|
+
return Module(named=named, array=array, static=static)
|
|
66
|
+
|
|
67
|
+
Block = hax.Axis("block", 3)
|
|
68
|
+
E = hax.Axis("E", 4)
|
|
69
|
+
|
|
70
|
+
initial_named = hax.random.uniform(jax.random.PRNGKey(0), (Block, E))
|
|
71
|
+
arrays = jax.numpy.arange(Block.size)
|
|
72
|
+
|
|
73
|
+
stacked = Stacked.init(Block, Module)(named=initial_named, array=arrays, static=2)
|
|
74
|
+
|
|
75
|
+
layer = stacked.get_layer(1)
|
|
76
|
+
|
|
77
|
+
assert isinstance(layer, Module)
|
|
78
|
+
assert layer.static == 2
|
|
79
|
+
assert layer.named.axes == (E,)
|
|
80
|
+
assert hax.all(layer.named == initial_named["block", 1])
|
|
81
|
+
assert hax.all(layer.array == arrays[1])
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_get_layer_blockseq():
|
|
85
|
+
class Module(eqx.Module):
|
|
86
|
+
named: hax.NamedArray
|
|
87
|
+
array: jax.Array
|
|
88
|
+
static: int = eqx.static_field()
|
|
89
|
+
|
|
90
|
+
def __call__(self, x, *, key): # pragma: no cover - unused in this test
|
|
91
|
+
return x + self.array + self.static
|
|
92
|
+
|
|
93
|
+
@staticmethod
|
|
94
|
+
def init(named, array, static):
|
|
95
|
+
return Module(named=named, array=array, static=static)
|
|
96
|
+
|
|
97
|
+
Block = hax.Axis("block", 3)
|
|
98
|
+
E = hax.Axis("E", 4)
|
|
99
|
+
|
|
100
|
+
initial_named = hax.random.uniform(jax.random.PRNGKey(0), (Block, E))
|
|
101
|
+
arrays = jax.numpy.arange(Block.size)
|
|
102
|
+
|
|
103
|
+
seq = BlockSeq.init(Block, Module)(named=initial_named, array=arrays, static=2)
|
|
104
|
+
|
|
105
|
+
layer = seq.get_layer(2)
|
|
106
|
+
|
|
107
|
+
assert isinstance(layer, Module)
|
|
108
|
+
assert layer.static == 2
|
|
109
|
+
assert hax.all(layer.named == initial_named["block", 2])
|
|
110
|
+
assert hax.all(layer.array == arrays[2])
|
|
111
|
+
|
|
112
|
+
|
|
54
113
|
def test_vmap():
|
|
55
114
|
class Module(eqx.Module):
|
|
56
115
|
weight: hax.NamedArray
|
|
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
|
|
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
|