haliax 1.4.dev355__tar.gz → 1.4.dev359__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.dev355 → haliax-1.4.dev359}/PKG-INFO +1 -1
- haliax-1.4.dev359/src/haliax/__about__.py +1 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/__init__.py +3 -3
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/core.py +40 -18
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/hof.py +4 -2
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/nn/__init__.py +2 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/nn/activations.py +10 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/nn/attention.py +1 -1
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/nn/normalization.py +1 -1
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/ops.py +3 -3
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/random.py +3 -3
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/specialized_fns.py +1 -1
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/wrap.py +2 -2
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_nn.py +52 -0
- haliax-1.4.dev355/src/haliax/__about__.py +0 -1
- {haliax-1.4.dev355 → haliax-1.4.dev359}/.coveragerc +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/.flake8 +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/.github/workflows/publish_dev.yaml +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/.github/workflows/run_pre_commit.yaml +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/.github/workflows/run_quick_levanter_tests.yaml +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/.github/workflows/run_tests.yaml +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/.gitignore +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/.pre-commit-config.yaml +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/.readthedocs.yaml +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/CONTRIBUTING.md +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/LICENSE +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/README.md +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/api.md +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/broadcasting.md +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/cheatsheet.md +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/css/material.css +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/css/mkdocstrings.css +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/faq.md +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/figures/data_parallel_mesh.png +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/figures/data_parallel_mesh_replicated.png +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/figures/device_mesh_1d.png +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/figures/device_mesh_1d_zero.png +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/figures/device_mesh_2d.png +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/figures/device_mesh_2d_batch_partitioned.png +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/figures/device_mesh_2d_data_replicated.png +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/figures/device_mesh_2d_data_replicated_mlp_partitioned.png +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/figures/device_mesh_2d_intermediate_fully_partitioned.png +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/figures/device_mesh_2d_zero.png +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/fp8.md +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/index.md +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/indexing.md +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/matmul.md +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/nn.md +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/partitioning.md +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/rearrange.ipynb +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/rearrange.md +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/requirements.txt +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/scan.md +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/state-dict.md +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/tutorial.md +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/docs/vmap.md +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/mkdocs.yml +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/pyproject.toml +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/_src/__init__.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/_src/compile_utils.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/_src/dot.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/_src/einsum.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/_src/fp8.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/_src/parsing.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/_src/rearrange.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/_src/scan.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/_src/state_dict.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/_src/util.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/axis.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/debug.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/jax_utils.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/nn/conv.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/nn/dropout.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/nn/embedding.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/nn/linear.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/nn/loss.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/nn/mlp.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/nn/pool.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/nn/scan.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/partitioning.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/quantization.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/state_dict.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/tree_util.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/types.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/src/haliax/util.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/core_test.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_attention.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_axis.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_conv.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_debug.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_dot.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_einsum.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_fp8.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_hof.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_int8.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_ops.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_parsing.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_partitioning.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_pool.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_random.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_rearrange.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_scan.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_scatter_gather.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_specialized_fns.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_state_dict.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_tree_util.py +0 -0
- {haliax-1.4.dev355 → haliax-1.4.dev359}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: haliax
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.dev359
|
|
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.dev359"
|
|
@@ -212,7 +212,7 @@ def repeat(
|
|
|
212
212
|
a: NamedArray, repeats: int | jnp.ndarray, axis: AxisSelector, total_repeat_length: Optional[int] = None
|
|
213
213
|
) -> NamedArray:
|
|
214
214
|
"""Version of [jax.numpy.repeat][] that returns a NamedArray"""
|
|
215
|
-
index = a.
|
|
215
|
+
index = a.axis_indices(axis)
|
|
216
216
|
if index is None:
|
|
217
217
|
raise ValueError(f"Axis {axis} not found in array {a}")
|
|
218
218
|
|
|
@@ -235,7 +235,7 @@ def tile(a: NamedArray, reps: dict[AxisSelector, int]) -> NamedArray:
|
|
|
235
235
|
new_dims = []
|
|
236
236
|
dim_reps = [1] * len(a.axes)
|
|
237
237
|
for ax, i in reps.items():
|
|
238
|
-
index = a.
|
|
238
|
+
index = a.axis_indices(ax)
|
|
239
239
|
if index is None:
|
|
240
240
|
new_dims.append(Axis(axis_name(ax), i))
|
|
241
241
|
else:
|
|
@@ -265,7 +265,7 @@ def concatenate(axis: AxisSelector, arrays: Sequence[NamedArray]) -> NamedArray:
|
|
|
265
265
|
if len(arrays) == 0:
|
|
266
266
|
return zeros(axis)
|
|
267
267
|
|
|
268
|
-
axis_index = arrays[0].
|
|
268
|
+
axis_index = arrays[0].axis_indices(aname)
|
|
269
269
|
if axis_index is None:
|
|
270
270
|
raise ValueError(f"Axis {aname} not found in 0th array {arrays[0]}")
|
|
271
271
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import contextlib
|
|
2
2
|
import functools as ft
|
|
3
3
|
import typing
|
|
4
|
+
import warnings
|
|
4
5
|
from dataclasses import dataclass
|
|
5
6
|
from math import prod
|
|
6
7
|
from types import EllipsisType
|
|
@@ -142,7 +143,7 @@ class NamedArray:
|
|
|
142
143
|
|
|
143
144
|
def has_axis(self, axis: AxisSelection) -> bool:
|
|
144
145
|
"""Returns true if the given axis is present in this NamedArray."""
|
|
145
|
-
return self.
|
|
146
|
+
return self.axis_indices(axis) is not None
|
|
146
147
|
|
|
147
148
|
@overload
|
|
148
149
|
def axis_size(self, axis: AxisSelector) -> int: # type: ignore
|
|
@@ -156,7 +157,7 @@ class NamedArray:
|
|
|
156
157
|
"""
|
|
157
158
|
Returns the size of the given axis, or a tuple of sizes if given multiple axes.
|
|
158
159
|
"""
|
|
159
|
-
indices = self.
|
|
160
|
+
indices = self.axis_indices(axis)
|
|
160
161
|
if isinstance(indices, int):
|
|
161
162
|
return self.axes[indices].size
|
|
162
163
|
elif indices is None:
|
|
@@ -184,7 +185,7 @@ class NamedArray:
|
|
|
184
185
|
|
|
185
186
|
Raises a ValueError if any of the axes are not found.
|
|
186
187
|
"""
|
|
187
|
-
indices = self.
|
|
188
|
+
indices = self.axis_indices(axes)
|
|
188
189
|
if isinstance(indices, int):
|
|
189
190
|
return self.axes[indices]
|
|
190
191
|
elif indices is None:
|
|
@@ -235,6 +236,27 @@ class NamedArray:
|
|
|
235
236
|
For a single axis, returns an int corresponding to the index of the axis.
|
|
236
237
|
For multiple axes, returns a tuple of ints corresponding to the indices of the axes.
|
|
237
238
|
|
|
239
|
+
If the axis is not present, returns None for that position
|
|
240
|
+
"""
|
|
241
|
+
warnings.warn(
|
|
242
|
+
"_lookup_indices() is deprecated, use axis_indices() instead",
|
|
243
|
+
DeprecationWarning,
|
|
244
|
+
)
|
|
245
|
+
return self.axis_indices(axis)
|
|
246
|
+
|
|
247
|
+
@overload
|
|
248
|
+
def axis_indices(self, axis: AxisSelector) -> Optional[int]: # type: ignore
|
|
249
|
+
...
|
|
250
|
+
|
|
251
|
+
@overload
|
|
252
|
+
def axis_indices(self, axis: Sequence[AxisSelector]) -> Tuple[Optional[int], ...]:
|
|
253
|
+
...
|
|
254
|
+
|
|
255
|
+
def axis_indices(self, axis: AxisSelection) -> Union[Optional[int], Tuple[Optional[int], ...]]:
|
|
256
|
+
"""
|
|
257
|
+
For a single axis, returns an int corresponding to the index of the axis.
|
|
258
|
+
For multiple axes, returns a tuple of ints corresponding to the indices of the axes.
|
|
259
|
+
|
|
238
260
|
If the axis is not present, returns None for that position
|
|
239
261
|
"""
|
|
240
262
|
if isinstance(axis, Axis):
|
|
@@ -262,7 +284,7 @@ class NamedArray:
|
|
|
262
284
|
except ValueError:
|
|
263
285
|
return None
|
|
264
286
|
else:
|
|
265
|
-
return tuple(self.
|
|
287
|
+
return tuple(self.axis_indices(a) for a in axis)
|
|
266
288
|
|
|
267
289
|
# Axis rearrangement
|
|
268
290
|
@typing.overload
|
|
@@ -675,7 +697,7 @@ def take(array: NamedArray, axis: AxisSelector, index: Union[int, NamedArray]) -
|
|
|
675
697
|
|
|
676
698
|
if index is a NamedArray, then those axes are added to the output array
|
|
677
699
|
"""
|
|
678
|
-
axis_index = array.
|
|
700
|
+
axis_index = array.axis_indices(axis)
|
|
679
701
|
if axis_index is None:
|
|
680
702
|
raise ValueError(f"axis {axis} not found in {array}")
|
|
681
703
|
|
|
@@ -696,14 +718,14 @@ def take(array: NamedArray, axis: AxisSelector, index: Union[int, NamedArray]) -
|
|
|
696
718
|
|
|
697
719
|
if intersecting_axes:
|
|
698
720
|
# if the eliminated axis is also in the index, we rename it to a dummy axis that we can broadcast over it
|
|
699
|
-
need_to_use_dummy_axis = index.
|
|
721
|
+
need_to_use_dummy_axis = index.axis_indices(axis.name) is not None
|
|
700
722
|
if need_to_use_dummy_axis:
|
|
701
723
|
index = index.rename({axis.name: "__DUMMY_" + axis.name})
|
|
702
724
|
array = haliax.broadcast_to(array, index.axes, ensure_order=False, enforce_no_extra_axes=False)
|
|
703
725
|
new_axes = eliminate_axes(array.axes, axis)
|
|
704
726
|
index = haliax.broadcast_to(index, new_axes, ensure_order=True, enforce_no_extra_axes=True)
|
|
705
727
|
|
|
706
|
-
axis_index = array.
|
|
728
|
+
axis_index = array.axis_indices(axis) # if it moved
|
|
707
729
|
index_array = jnp.expand_dims(index.array, axis=axis_index)
|
|
708
730
|
new_array = jnp.take_along_axis(array.array, index_array, axis=axis_index)
|
|
709
731
|
new_array = jnp.squeeze(new_array, axis=axis_index)
|
|
@@ -792,7 +814,7 @@ def _slice_old(
|
|
|
792
814
|
Note:
|
|
793
815
|
This method is basically a wrapper around jax.lax.dynamic_slice_in_dim.
|
|
794
816
|
"""
|
|
795
|
-
axis_index = array.
|
|
817
|
+
axis_index = array.axis_indices(axis)
|
|
796
818
|
if axis_index is None:
|
|
797
819
|
raise ValueError(f"axis {axis} not found in {array}")
|
|
798
820
|
|
|
@@ -824,7 +846,7 @@ def _slice_new(
|
|
|
824
846
|
new_lengths = [axis.size for axis in array.axes]
|
|
825
847
|
|
|
826
848
|
for axis, s in start.items():
|
|
827
|
-
axis_index = array.
|
|
849
|
+
axis_index = array.axis_indices(axis_name(axis))
|
|
828
850
|
if axis_index is None:
|
|
829
851
|
raise ValueError(f"axis {axis} not found in {array}")
|
|
830
852
|
|
|
@@ -883,14 +905,14 @@ def updated_slice(
|
|
|
883
905
|
f = updated_slice
|
|
884
906
|
for axis_name in map_axes:
|
|
885
907
|
# make sure that axis_name is in `array`. otherwise it doesn't make sense to vmap over it
|
|
886
|
-
if array.
|
|
908
|
+
if array.axis_indices(axis_name) is None:
|
|
887
909
|
raise ValueError(f"axis {axis_name} not found in original array's axes: {array.shape}")
|
|
888
910
|
f = haliax.vmap(f, axis=axis_name)
|
|
889
911
|
return f(array, start, update)
|
|
890
912
|
|
|
891
913
|
array_slice_indices = [0] * len(array.axes)
|
|
892
914
|
for axis, s in start.items():
|
|
893
|
-
axis_index = array.
|
|
915
|
+
axis_index = array.axis_indices(haliax.axis_name(axis))
|
|
894
916
|
if axis_index is None:
|
|
895
917
|
raise ValueError(f"axis {axis} not found in {array}")
|
|
896
918
|
if isinstance(s, NamedArray): # this can happen in the vmap case
|
|
@@ -900,7 +922,7 @@ def updated_slice(
|
|
|
900
922
|
|
|
901
923
|
array_slice_indices[axis_index] = s
|
|
902
924
|
total_length = array.axes[axis_index].size
|
|
903
|
-
update_axis = update.
|
|
925
|
+
update_axis = update.axis_indices(haliax.axis_name(axis))
|
|
904
926
|
|
|
905
927
|
# if s is a tracer we can't check the size
|
|
906
928
|
if update_axis is None:
|
|
@@ -923,7 +945,7 @@ def updated_slice(
|
|
|
923
945
|
)
|
|
924
946
|
broadcasted_axes = []
|
|
925
947
|
for ax in array.axes:
|
|
926
|
-
upd_ax = update.
|
|
948
|
+
upd_ax = update.axis_indices(ax.name)
|
|
927
949
|
broadcasted_axes.append(ax if upd_ax is None else update.axes[upd_ax])
|
|
928
950
|
update = haliax.broadcast_to(update, broadcasted_axes, enforce_no_extra_axes=True)
|
|
929
951
|
upd_arr = update.array
|
|
@@ -967,7 +989,7 @@ def _compute_new_axes_and_slices_for_index(
|
|
|
967
989
|
index_axis_names = set()
|
|
968
990
|
|
|
969
991
|
for axis, slice_ in slices.items():
|
|
970
|
-
axis_index = array.
|
|
992
|
+
axis_index = array.axis_indices(axis)
|
|
971
993
|
if axis_index is None:
|
|
972
994
|
raise ValueError(f"axis {axis} not found in {array}")
|
|
973
995
|
if isinstance(slice_, py_slice) or isinstance(slice_, dslice) or is_pallas_dslice(slice_):
|
|
@@ -1111,7 +1133,7 @@ def split(a: NamedArray, axis: AxisSelector, new_axes: Sequence[Axis]) -> Sequen
|
|
|
1111
1133
|
new_axes (Sequence[Axis]): the axes to split into. Must have the same total length as the axis being split.
|
|
1112
1134
|
"""
|
|
1113
1135
|
# check the lengths of the new axes
|
|
1114
|
-
index = a.
|
|
1136
|
+
index = a.axis_indices(axis)
|
|
1115
1137
|
if index is None:
|
|
1116
1138
|
raise ValueError(f"Axis {axis} not found in {a.axes}")
|
|
1117
1139
|
|
|
@@ -1136,7 +1158,7 @@ def unbind(array: NamedArray, axis: AxisSelector) -> List[NamedArray]:
|
|
|
1136
1158
|
Unbind an array along an axis, returning a list of NamedArrays, one for each position on that axis.
|
|
1137
1159
|
Analogous to torch.unbind or np.rollaxis
|
|
1138
1160
|
"""
|
|
1139
|
-
axis_index = array.
|
|
1161
|
+
axis_index = array.axis_indices(axis)
|
|
1140
1162
|
if axis_index is None:
|
|
1141
1163
|
raise ValueError(f"axis {axis} not found in {array}")
|
|
1142
1164
|
new_axes = array.axes[:axis_index] + array.axes[axis_index + 1 :]
|
|
@@ -1153,7 +1175,7 @@ def roll(array: NamedArray, shift: Union[int, Tuple[int, ...]], axis: AxisSelect
|
|
|
1153
1175
|
"""
|
|
1154
1176
|
Roll an array along an axis or axes. Analogous to np.roll
|
|
1155
1177
|
"""
|
|
1156
|
-
axis_indices = array.
|
|
1178
|
+
axis_indices = array.axis_indices(axis)
|
|
1157
1179
|
if axis_indices is None:
|
|
1158
1180
|
raise ValueError(f"axis {axis} not found in {array}")
|
|
1159
1181
|
return NamedArray(jnp.roll(array.array, shift, axis_indices), array.axes)
|
|
@@ -1331,7 +1353,7 @@ def unflatten_axis(array: NamedArray, axis: AxisSelector, new_axes: AxisSpec) ->
|
|
|
1331
1353
|
"""
|
|
1332
1354
|
Split an axis into a sequence of axes. The old axis must have the same size as the product of the new axes.
|
|
1333
1355
|
"""
|
|
1334
|
-
old_index = array.
|
|
1356
|
+
old_index = array.axis_indices(axis)
|
|
1335
1357
|
if old_index is None:
|
|
1336
1358
|
raise ValueError(f"Axis {axis} not found in {array}")
|
|
1337
1359
|
|
|
@@ -61,8 +61,10 @@ def vmap(
|
|
|
61
61
|
for ax in reversed(axes):
|
|
62
62
|
mapped = vmap(mapped, ax, default=default, args=args, kwargs=kwargs)
|
|
63
63
|
return mapped
|
|
64
|
-
|
|
64
|
+
elif len(axes) == 1: # type: ignore
|
|
65
65
|
axis = axes[0]
|
|
66
|
+
else:
|
|
67
|
+
return fn
|
|
66
68
|
|
|
67
69
|
signature = inspect.signature(fn)
|
|
68
70
|
|
|
@@ -83,7 +85,7 @@ def vmap(
|
|
|
83
85
|
|
|
84
86
|
def _index_of_batch_axis(array, default):
|
|
85
87
|
if isinstance(array, NamedArray):
|
|
86
|
-
return array.
|
|
88
|
+
return array.axis_indices(axis)
|
|
87
89
|
elif callable(default):
|
|
88
90
|
return default(array)
|
|
89
91
|
else:
|
|
@@ -23,6 +23,7 @@ from .activations import (
|
|
|
23
23
|
quick_gelu,
|
|
24
24
|
relu,
|
|
25
25
|
relu6,
|
|
26
|
+
relu_squared,
|
|
26
27
|
selu,
|
|
27
28
|
sigmoid,
|
|
28
29
|
silu,
|
|
@@ -94,6 +95,7 @@ __all__ = [
|
|
|
94
95
|
"quick_gelu",
|
|
95
96
|
"glu",
|
|
96
97
|
"relu6",
|
|
98
|
+
"relu_squared",
|
|
97
99
|
"sigmoid",
|
|
98
100
|
"soft_sign",
|
|
99
101
|
"softplus",
|
|
@@ -87,3 +87,13 @@ def glu(x: NamedArray, axis: Axis) -> NamedArray:
|
|
|
87
87
|
|
|
88
88
|
def quick_gelu(x):
|
|
89
89
|
return x * sigmoid(1.702 * x)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def relu_squared(x: A) -> A:
|
|
94
|
+
"""ReLU squared activation function. jnp.square(jnp.maximum(0, x))"""
|
|
95
|
+
|
|
96
|
+
def _fn(a):
|
|
97
|
+
return jnp.square(jnn.relu(a))
|
|
98
|
+
|
|
99
|
+
return typing.cast(A, wrap_elemwise_unary(_fn, x))
|
|
@@ -111,7 +111,7 @@ def dot_product_attention(
|
|
|
111
111
|
KPos = ensure_tuple(key.resolve_axis(KPos))
|
|
112
112
|
# any axis in KPos that's in query is a problem
|
|
113
113
|
for axis in KPos:
|
|
114
|
-
if
|
|
114
|
+
if query.has_axis(axis):
|
|
115
115
|
raise ValueError(
|
|
116
116
|
f"Axis {axis} in KPos is also in query. Attended-to axes must be distinct from query axis"
|
|
117
117
|
)
|
|
@@ -163,7 +163,7 @@ def standardize(
|
|
|
163
163
|
"""Analogous to [jax.nn.standardize][], but with support for NamedArrays."""
|
|
164
164
|
x, mean, variance, where = haliax.broadcast_arrays(x, mean, variance, where) # type: ignore
|
|
165
165
|
raw_x, mean, variance, where = unwrap_namedarrays(x, mean, variance, where)
|
|
166
|
-
axis_indices = x.
|
|
166
|
+
axis_indices = x.axis_indices(axis)
|
|
167
167
|
|
|
168
168
|
plain = jnn.standardize(raw_x, axis_indices, mean=mean, variance=variance, epsilon=epsilon, where=where)
|
|
169
169
|
return NamedArray(plain, x.axes)
|
|
@@ -11,8 +11,8 @@ from .jax_utils import is_scalarish
|
|
|
11
11
|
|
|
12
12
|
def trace(array: NamedArray, axis1: AxisSelector, axis2: AxisSelector, offset=0, dtype=None) -> NamedArray:
|
|
13
13
|
"""Compute the trace of an array along two named axes."""
|
|
14
|
-
a1_index = array.
|
|
15
|
-
a2_index = array.
|
|
14
|
+
a1_index = array.axis_indices(axis1)
|
|
15
|
+
a2_index = array.axis_indices(axis2)
|
|
16
16
|
|
|
17
17
|
if a1_index is None:
|
|
18
18
|
raise ValueError(f"Axis {axis1} not found in array. Available axes: {array.axes}")
|
|
@@ -139,7 +139,7 @@ def pad_left(array: NamedArray, axis: Axis, new_axis: Axis, value=0) -> NamedArr
|
|
|
139
139
|
if amount_to_pad_to < 0:
|
|
140
140
|
raise ValueError(f"Cannot pad {axis} to {new_axis}")
|
|
141
141
|
|
|
142
|
-
idx = array.
|
|
142
|
+
idx = array.axis_indices(axis)
|
|
143
143
|
|
|
144
144
|
padding = [(0, 0)] * array.ndim
|
|
145
145
|
if idx is None:
|
|
@@ -218,7 +218,7 @@ def choice(
|
|
|
218
218
|
:return: Array with shape `shape` + (`a.axes` - `axis`)
|
|
219
219
|
"""
|
|
220
220
|
|
|
221
|
-
index = a.
|
|
221
|
+
index = a.axis_indices(axis)
|
|
222
222
|
assert index is not None, f"axis {axis} not in a"
|
|
223
223
|
|
|
224
224
|
shape = ensure_tuple(shape)
|
|
@@ -261,7 +261,7 @@ def categorical(key, logits: NamedArray, axis: AxisSelector, shape: Optional[Axi
|
|
|
261
261
|
|
|
262
262
|
logits = logits.broadcast_axis(shape)
|
|
263
263
|
|
|
264
|
-
index = logits.
|
|
264
|
+
index = logits.axis_indices(axis)
|
|
265
265
|
assert index is not None, f"axis {axis} not in logits"
|
|
266
266
|
|
|
267
267
|
jax_shape = to_jax_shape(shape)
|
|
@@ -280,7 +280,7 @@ def gumbel(key, shape: AxisSpec, dtype=float):
|
|
|
280
280
|
|
|
281
281
|
@named_call
|
|
282
282
|
def permutation(key, x: NamedArray, axis: AxisSelector, independent: bool = False):
|
|
283
|
-
axis_index = x.
|
|
283
|
+
axis_index = x.axis_indices(axis)
|
|
284
284
|
jax_array = jrandom.permutation(key, x.array, axis_index, independent=independent)
|
|
285
285
|
return haliax.auto_sharded(NamedArray(jax_array, x.axes))
|
|
286
286
|
|
|
@@ -22,7 +22,7 @@ def top_k(
|
|
|
22
22
|
NamedArray: array with the top k elements along the given axis
|
|
23
23
|
NamedArray: array with the top k elements' indices along the given axis
|
|
24
24
|
"""
|
|
25
|
-
pos = arr.
|
|
25
|
+
pos = arr.axis_indices(axis)
|
|
26
26
|
if pos is None:
|
|
27
27
|
raise ValueError(f"Axis {axis} not found in {arr}")
|
|
28
28
|
new_array = jnp.moveaxis(arr.array, pos, -1) # move axis to the last position
|
|
@@ -52,7 +52,7 @@ def wrap_reduction_call(
|
|
|
52
52
|
axis = ensure_tuple(axis)
|
|
53
53
|
if single_axis_only and len(axis) > 1:
|
|
54
54
|
raise ValueError(f"{fn.__name__} only supports a single axis")
|
|
55
|
-
indices = a.
|
|
55
|
+
indices = a.axis_indices(axis)
|
|
56
56
|
if indices is None or any(x is None for x in indices):
|
|
57
57
|
raise ValueError(f"axis {axis} is not in {a.axes}")
|
|
58
58
|
new_axes = [ax for ax in a.axes if not selects_axis(axis, ax)]
|
|
@@ -74,7 +74,7 @@ def wrap_axiswise_call(fn, a, axis: Optional[AxisSelection], *, single_axis_only
|
|
|
74
74
|
if axis is None:
|
|
75
75
|
return fn(a.array, axis=None, **kwargs)
|
|
76
76
|
else:
|
|
77
|
-
indices = ensure_tuple(a.
|
|
77
|
+
indices = ensure_tuple(a.axis_indices(axis))
|
|
78
78
|
if any(x is None for x in indices):
|
|
79
79
|
raise ValueError(f"axis {axis} is not in {a.axes}")
|
|
80
80
|
if len(indices) == 1:
|
|
@@ -147,3 +147,55 @@ def test_linear_has_no_function_leaves_by_default():
|
|
|
147
147
|
|
|
148
148
|
hax_linear = hax.nn.Linear.init((H, C, W), E, key=jrandom.PRNGKey(0))
|
|
149
149
|
assert all(not isinstance(v, Callable) for v in jax.tree_util.tree_leaves(hax_linear)) # type: ignore
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@pytest.mark.parametrize(
|
|
153
|
+
"input_data, axes",
|
|
154
|
+
[
|
|
155
|
+
(jnp.array([-2.0, -1.0, 0.0, 1.0, 2.0]), (hax.Axis("X", 5),)),
|
|
156
|
+
(jnp.array([[1.0, -1.0], [0.0, 2.0]]), (hax.Axis("Y", 2), hax.Axis("Z", 2))),
|
|
157
|
+
(jnp.array([jnp.nan, 1.0, -1.0]), (hax.Axis("A", 3),)),
|
|
158
|
+
(jnp.array([jnp.inf, -jnp.inf, 0.0]), (hax.Axis("B", 3),)),
|
|
159
|
+
],
|
|
160
|
+
)
|
|
161
|
+
@pytest.mark.parametrize("dtype", [jnp.float16, jnp.float32, jnp.bfloat16])
|
|
162
|
+
@pytest.mark.parametrize("use_jit", [False, True])
|
|
163
|
+
def test_relu_squared_robust(input_data, axes, dtype, use_jit):
|
|
164
|
+
input_data = input_data.astype(dtype)
|
|
165
|
+
x = hax.named(input_data, axes)
|
|
166
|
+
|
|
167
|
+
# Manually compute the expected output using the base JAX functions
|
|
168
|
+
expected_raw = jnp.square(jax.nn.relu(input_data))
|
|
169
|
+
expected = hax.named(expected_raw, axes)
|
|
170
|
+
|
|
171
|
+
f = hax.nn.relu_squared
|
|
172
|
+
if use_jit:
|
|
173
|
+
f = hax.named_jit(f)
|
|
174
|
+
|
|
175
|
+
# Apply the relu_squared function
|
|
176
|
+
actual = f(x)
|
|
177
|
+
|
|
178
|
+
# Check that the output is a NamedArray with the correct axes and dtype
|
|
179
|
+
assert isinstance(actual, hax.NamedArray)
|
|
180
|
+
assert actual.axes == expected.axes
|
|
181
|
+
assert actual.dtype == expected.dtype
|
|
182
|
+
|
|
183
|
+
# Check that the values are correct, handling NaNs correctly
|
|
184
|
+
assert jnp.allclose(actual.array, expected.array, equal_nan=True)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@pytest.mark.parametrize("use_jit", [False, True])
|
|
188
|
+
def test_relu_squared_scalar(use_jit):
|
|
189
|
+
f = hax.nn.relu_squared
|
|
190
|
+
if use_jit:
|
|
191
|
+
f = jax.jit(f)
|
|
192
|
+
|
|
193
|
+
x = 5.0
|
|
194
|
+
expected = 25.0
|
|
195
|
+
actual = f(x)
|
|
196
|
+
assert jnp.allclose(actual, expected)
|
|
197
|
+
|
|
198
|
+
x_neg = -5.0
|
|
199
|
+
expected_neg = 0.0
|
|
200
|
+
actual_neg = f(x_neg)
|
|
201
|
+
assert jnp.allclose(actual_neg, expected_neg)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.4.dev355"
|
|
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
|