haliax 1.4.dev413__tar.gz → 1.4.dev419__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.
Files changed (130) hide show
  1. {haliax-1.4.dev413 → haliax-1.4.dev419}/.agents/projects/api_parity.md +12 -12
  2. {haliax-1.4.dev413 → haliax-1.4.dev419}/AUTHORS.md +1 -0
  3. {haliax-1.4.dev413 → haliax-1.4.dev419}/PKG-INFO +2 -1
  4. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/api.md +15 -0
  5. {haliax-1.4.dev413 → haliax-1.4.dev419}/pyproject.toml +1 -2
  6. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/__about__.py +1 -1
  7. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/__init__.py +51 -12
  8. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/_src/state_dict.py +1 -1
  9. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/core.py +24 -4
  10. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/fft.py +4 -1
  11. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/nn/__init__.py +18 -7
  12. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/nn/linear.py +1 -1
  13. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/ops.py +2 -4
  14. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/partitioning.py +101 -62
  15. haliax-1.4.dev419/src/haliax/poly.py +304 -0
  16. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/core_test.py +46 -0
  17. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_bitwise_ops.py +4 -0
  18. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_fft.py +5 -3
  19. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_moe_linear.py +3 -2
  20. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_nan_reductions.py +4 -0
  21. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_nn.py +18 -0
  22. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_partitioning.py +15 -27
  23. haliax-1.4.dev419/tests/test_poly_ops.py +134 -0
  24. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_utils.py +5 -2
  25. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_visualize_sharding.py +5 -2
  26. haliax-1.4.dev419/uv.lock +1951 -0
  27. haliax-1.4.dev413/uv.lock +0 -1711
  28. {haliax-1.4.dev413 → haliax-1.4.dev419}/.coveragerc +0 -0
  29. {haliax-1.4.dev413 → haliax-1.4.dev419}/.flake8 +0 -0
  30. {haliax-1.4.dev413 → haliax-1.4.dev419}/.github/workflows/publish_dev.yaml +0 -0
  31. {haliax-1.4.dev413 → haliax-1.4.dev419}/.github/workflows/run_pre_commit.yaml +0 -0
  32. {haliax-1.4.dev413 → haliax-1.4.dev419}/.github/workflows/run_quick_levanter_tests.yaml +0 -0
  33. {haliax-1.4.dev413 → haliax-1.4.dev419}/.github/workflows/run_tests.yaml +0 -0
  34. {haliax-1.4.dev413 → haliax-1.4.dev419}/.gitignore +0 -0
  35. {haliax-1.4.dev413 → haliax-1.4.dev419}/.playbooks/add-types.md +0 -0
  36. {haliax-1.4.dev413 → haliax-1.4.dev419}/.playbooks/wrap-non-named.md +0 -0
  37. {haliax-1.4.dev413 → haliax-1.4.dev419}/.pre-commit-config.yaml +0 -0
  38. {haliax-1.4.dev413 → haliax-1.4.dev419}/.readthedocs.yaml +0 -0
  39. {haliax-1.4.dev413 → haliax-1.4.dev419}/AGENTS.md +0 -0
  40. {haliax-1.4.dev413 → haliax-1.4.dev419}/CONTRIBUTING.md +0 -0
  41. {haliax-1.4.dev413 → haliax-1.4.dev419}/CONTRIBUTORS.md +0 -0
  42. {haliax-1.4.dev413 → haliax-1.4.dev419}/LICENSE +0 -0
  43. {haliax-1.4.dev413 → haliax-1.4.dev419}/README.md +0 -0
  44. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/broadcasting.md +0 -0
  45. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/cheatsheet.md +0 -0
  46. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/css/material.css +0 -0
  47. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/css/mkdocstrings.css +0 -0
  48. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/faq.md +0 -0
  49. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/figures/data_parallel_mesh.png +0 -0
  50. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/figures/data_parallel_mesh_replicated.png +0 -0
  51. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/figures/device_mesh_1d.png +0 -0
  52. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/figures/device_mesh_1d_zero.png +0 -0
  53. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/figures/device_mesh_2d.png +0 -0
  54. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/figures/device_mesh_2d_batch_partitioned.png +0 -0
  55. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/figures/device_mesh_2d_data_replicated.png +0 -0
  56. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/figures/device_mesh_2d_data_replicated_mlp_partitioned.png +0 -0
  57. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/figures/device_mesh_2d_intermediate_fully_partitioned.png +0 -0
  58. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/figures/device_mesh_2d_zero.png +0 -0
  59. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/fp8.md +0 -0
  60. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/index.md +0 -0
  61. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/indexing.md +0 -0
  62. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/matmul.md +0 -0
  63. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/nn.md +0 -0
  64. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/partitioning.md +0 -0
  65. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/primer.md +0 -0
  66. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/rearrange.ipynb +0 -0
  67. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/rearrange.md +0 -0
  68. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/requirements.txt +0 -0
  69. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/scan.md +0 -0
  70. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/state-dict.md +0 -0
  71. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/tutorial.md +0 -0
  72. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/typing.md +0 -0
  73. {haliax-1.4.dev413 → haliax-1.4.dev419}/docs/vmap.md +0 -0
  74. {haliax-1.4.dev413 → haliax-1.4.dev419}/etc/license_header.txt +0 -0
  75. {haliax-1.4.dev413 → haliax-1.4.dev419}/mkdocs.yml +0 -0
  76. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/_src/__init__.py +0 -0
  77. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/_src/compile_utils.py +0 -0
  78. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/_src/dot.py +0 -0
  79. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/_src/einsum.py +0 -0
  80. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/_src/fp8.py +0 -0
  81. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/_src/parsing.py +0 -0
  82. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/_src/rearrange.py +0 -0
  83. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/_src/scan.py +0 -0
  84. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/_src/util.py +0 -0
  85. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/axis.py +0 -0
  86. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/debug.py +0 -0
  87. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/field.py +0 -0
  88. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/haxtyping.py +0 -0
  89. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/hof.py +0 -0
  90. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/jax_utils.py +0 -0
  91. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/nn/activations.py +0 -0
  92. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/nn/attention.py +0 -0
  93. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/nn/conv.py +0 -0
  94. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/nn/dropout.py +0 -0
  95. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/nn/embedding.py +0 -0
  96. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/nn/loss.py +0 -0
  97. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/nn/mlp.py +0 -0
  98. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/nn/normalization.py +0 -0
  99. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/nn/pool.py +0 -0
  100. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/nn/scan.py +0 -0
  101. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/quantization.py +0 -0
  102. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/random.py +0 -0
  103. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/specialized_fns.py +0 -0
  104. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/state_dict.py +0 -0
  105. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/tree_util.py +0 -0
  106. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/types.py +0 -0
  107. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/util.py +0 -0
  108. {haliax-1.4.dev413 → haliax-1.4.dev419}/src/haliax/wrap.py +0 -0
  109. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_attention.py +0 -0
  110. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_axis.py +0 -0
  111. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_conv.py +0 -0
  112. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_debug.py +0 -0
  113. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_dot.py +0 -0
  114. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_dtype_typing.py +0 -0
  115. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_einsum.py +0 -0
  116. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_field.py +0 -0
  117. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_fp8.py +0 -0
  118. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_hof.py +0 -0
  119. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_int8.py +0 -0
  120. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_namedarray_typing.py +0 -0
  121. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_ops.py +0 -0
  122. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_parsing.py +0 -0
  123. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_pool.py +0 -0
  124. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_random.py +0 -0
  125. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_rearrange.py +0 -0
  126. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_scan.py +0 -0
  127. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_scatter_gather.py +0 -0
  128. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_specialized_fns.py +0 -0
  129. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_state_dict.py +0 -0
  130. {haliax-1.4.dev413 → haliax-1.4.dev419}/tests/test_tree_util.py +0 -0
@@ -115,15 +115,15 @@ APIs that don't translate well to named tensors are intentionally omitted here.
115
115
  - [ ] `permute_dims`
116
116
  - [ ] `piecewise`
117
117
  - [ ] `place`
118
- - [ ] `poly`
119
- - [ ] `polyadd`
120
- - [ ] `polyder`
121
- - [ ] `polydiv`
122
- - [ ] `polyfit`
123
- - [ ] `polyint`
124
- - [ ] `polymul`
125
- - [ ] `polysub`
126
- - [ ] `polyval`
118
+ - [x] `poly`
119
+ - [x] `polyadd`
120
+ - [x] `polyder`
121
+ - [x] `polydiv`
122
+ - [x] `polyfit`
123
+ - [x] `polyint`
124
+ - [x] `polymul`
125
+ - [x] `polysub`
126
+ - [x] `polyval`
127
127
  - [ ] `pow`
128
128
  - [ ] `promote_types`
129
129
  - [ ] `put`
@@ -134,7 +134,7 @@ APIs that don't translate well to named tensors are intentionally omitted here.
134
134
  - [ ] `resize`
135
135
  - [ ] `result_type`
136
136
  - [ ] `rollaxis`
137
- - [ ] `roots`
137
+ - [x] `roots`
138
138
  - [ ] `rot90`
139
139
  - [ ] `select`
140
140
  - [ ] `setdiff1d`
@@ -148,7 +148,7 @@ APIs that don't translate well to named tensors are intentionally omitted here.
148
148
  - [ ] `tri`
149
149
  - [ ] `tril_indices`
150
150
  - [ ] `tril_indices_from`
151
- - [ ] `trim_zeros`
151
+ - [x] `trim_zeros`
152
152
  - [ ] `triu_indices`
153
153
  - [ ] `triu_indices_from`
154
154
  - [ ] `union1d`
@@ -156,7 +156,7 @@ APIs that don't translate well to named tensors are intentionally omitted here.
156
156
  - [ ] `unravel_index`
157
157
  - [ ] `unstack`
158
158
  - [ ] `unwrap`
159
- - [ ] `vander`
159
+ - [x] `vander`
160
160
  - [ ] `vsplit`
161
161
  - [ ] `vstack`
162
162
 
@@ -3,3 +3,4 @@
3
3
  The Levanter Authors currently include:
4
4
 
5
5
  - The Board of Trustees of the Leland Stanford Junior University
6
+ - Open Athena AI Foundation, Inc.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: haliax
3
- Version: 1.4.dev413
3
+ Version: 1.4.dev419
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/
@@ -17,6 +17,7 @@ Classifier: Programming Language :: Python :: 3
17
17
  Requires-Python: >=3.10
18
18
  Requires-Dist: aqtp>=0.8.2
19
19
  Requires-Dist: equinox>=0.10.6
20
+ Requires-Dist: jax>=0.6.2
20
21
  Requires-Dist: jaxtyping>=0.2.20
21
22
  Requires-Dist: jmp>=0.0.4
22
23
  Requires-Dist: safetensors>=0.4.3
@@ -340,6 +340,21 @@ These are all more or less directly from JAX's NumPy API.
340
340
  ::: haliax.subtract
341
341
  ::: haliax.true_divide
342
342
 
343
+ ### Polynomial Operations
344
+
345
+ ::: haliax.poly
346
+ ::: haliax.polyadd
347
+ ::: haliax.polysub
348
+ ::: haliax.polymul
349
+ ::: haliax.polydiv
350
+ ::: haliax.polyint
351
+ ::: haliax.polyder
352
+ ::: haliax.polyval
353
+ ::: haliax.polyfit
354
+ ::: haliax.roots
355
+ ::: haliax.trim_zeros
356
+ ::: haliax.vander
357
+
343
358
  ### Other Operations
344
359
 
345
360
  ::: haliax.bincount
@@ -21,8 +21,7 @@ classifiers = [
21
21
  "Intended Audience :: Science/Research",
22
22
  ]
23
23
  dependencies = [
24
- # we require that you install jax yourself, since the extras vary by system.
25
- # jax = {version = ">=0.4.19,<0.5.0"}
24
+ "jax >= 0.6.2",
26
25
  "equinox>=0.10.6",
27
26
  "jaxtyping>=0.2.20",
28
27
  "jmp>=0.0.4",
@@ -3,4 +3,4 @@
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
5
 
6
- __version__ = "1.4.dev413"
6
+ __version__ = "1.4.dev419"
@@ -8,11 +8,8 @@ from typing import Optional, Sequence
8
8
 
9
9
  import jax
10
10
  import jax.numpy as jnp
11
+ from jax.typing import DTypeLike
11
12
 
12
- try:
13
- from jax.typing import DTypeLike
14
- except ImportError:
15
- from jax._src.typing import DTypeLike
16
13
 
17
14
  import haliax.debug as debug
18
15
  import haliax.nn as nn
@@ -96,6 +93,22 @@ from .ops import (
96
93
  bincount,
97
94
  where,
98
95
  )
96
+
97
+ from .poly import (
98
+ poly,
99
+ polyadd,
100
+ polysub,
101
+ polymul,
102
+ polydiv,
103
+ polyint,
104
+ polyder,
105
+ polyval,
106
+ polyfit,
107
+ roots,
108
+ trim_zeros,
109
+ vander,
110
+ )
111
+
99
112
  from .fft import (
100
113
  fft,
101
114
  fftfreq,
@@ -108,7 +121,7 @@ from .fft import (
108
121
  rfft,
109
122
  rfftfreq,
110
123
  )
111
- from .partitioning import auto_sharded, axis_mapping, fsdp, named_jit, shard, shard_with_axis_mapping
124
+ from .partitioning import auto_sharded, axis_mapping, fsdp, named_jit, set_mesh, shard, shard_with_axis_mapping
112
125
  from .specialized_fns import top_k
113
126
  from .types import Scalar
114
127
  from .util import is_named_array
@@ -715,7 +728,9 @@ def nanmean(
715
728
  where: Optional[NamedArray] = None,
716
729
  dtype: Optional[DTypeLike] = None,
717
730
  ) -> NamedArray:
718
- return wrap_reduction_call(jnp.nanmean, array, axis, where, single_axis_only=False, supports_where=True, dtype=dtype)
731
+ return wrap_reduction_call(
732
+ jnp.nanmean, array, axis, where, single_axis_only=False, supports_where=True, dtype=dtype
733
+ )
719
734
 
720
735
 
721
736
  def nanmin(
@@ -734,7 +749,9 @@ def nanprod(
734
749
  where: Optional[NamedArray] = None,
735
750
  dtype: Optional[DTypeLike] = None,
736
751
  ) -> NamedArray:
737
- return wrap_reduction_call(jnp.nanprod, array, axis, where, single_axis_only=False, supports_where=True, dtype=dtype)
752
+ return wrap_reduction_call(
753
+ jnp.nanprod, array, axis, where, single_axis_only=False, supports_where=True, dtype=dtype
754
+ )
738
755
 
739
756
 
740
757
  def nanstd(
@@ -745,7 +762,9 @@ def nanstd(
745
762
  ddof: int = 0,
746
763
  dtype: Optional[DTypeLike] = None,
747
764
  ) -> NamedArray:
748
- return wrap_reduction_call(jnp.nanstd, array, axis, where, single_axis_only=False, supports_where=True, dtype=dtype, ddof=ddof)
765
+ return wrap_reduction_call(
766
+ jnp.nanstd, array, axis, where, single_axis_only=False, supports_where=True, dtype=dtype, ddof=ddof
767
+ )
749
768
 
750
769
 
751
770
  def nansum(
@@ -755,7 +774,9 @@ def nansum(
755
774
  where: Optional[NamedArray] = None,
756
775
  dtype: Optional[DTypeLike] = None,
757
776
  ) -> NamedArray:
758
- return wrap_reduction_call(jnp.nansum, array, axis, where, single_axis_only=False, supports_where=True, dtype=dtype)
777
+ return wrap_reduction_call(
778
+ jnp.nansum, array, axis, where, single_axis_only=False, supports_where=True, dtype=dtype
779
+ )
759
780
 
760
781
 
761
782
  def nanvar(
@@ -766,7 +787,9 @@ def nanvar(
766
787
  ddof: int = 0,
767
788
  dtype: Optional[DTypeLike] = None,
768
789
  ) -> NamedArray:
769
- return wrap_reduction_call(jnp.nanvar, array, axis, where, single_axis_only=False, supports_where=True, dtype=dtype, ddof=ddof)
790
+ return wrap_reduction_call(
791
+ jnp.nanvar, array, axis, where, single_axis_only=False, supports_where=True, dtype=dtype, ddof=ddof
792
+ )
770
793
 
771
794
 
772
795
  # "Normalization" functions that use an axis but don't change the shape
@@ -807,14 +830,17 @@ def sort(a: NamedArray, axis: AxisSelector) -> NamedArray:
807
830
  return wrap_axiswise_call(jnp.sort, a, axis, single_axis_only=True)
808
831
 
809
832
 
810
- def argsort(a: NamedArray, axis: AxisSelector) -> NamedArray:
833
+ def argsort(a: NamedArray, axis: AxisSelector | None, *, stable: bool = False) -> NamedArray:
811
834
  """
812
835
  Named version of [jax.numpy.argsort](https://jax.readthedocs.io/en/latest/_autosummary/jax.numpy.argsort.html).
813
836
 
814
837
  If `axis` is None, the returned array will be a 1D array of indices that would sort the flattened array,
815
838
  identical to `jax.numpy.argsort(a.array)`.
839
+
840
+ Args:
841
+ stable: If ``True``, ensures that the indices of equal elements preserve their relative order.
816
842
  """
817
- return wrap_axiswise_call(jnp.argsort, a, axis, single_axis_only=True)
843
+ return wrap_axiswise_call(jnp.argsort, a, axis, single_axis_only=True, stable=stable)
818
844
 
819
845
 
820
846
  # elemwise binary ops
@@ -1226,6 +1252,18 @@ __all__ = [
1226
1252
  "clip",
1227
1253
  "tril",
1228
1254
  "triu",
1255
+ "poly",
1256
+ "polyadd",
1257
+ "polysub",
1258
+ "polymul",
1259
+ "polydiv",
1260
+ "polyint",
1261
+ "polyder",
1262
+ "polyval",
1263
+ "polyfit",
1264
+ "roots",
1265
+ "trim_zeros",
1266
+ "vander",
1229
1267
  "fft",
1230
1268
  "ifft",
1231
1269
  "hfft",
@@ -1311,4 +1349,5 @@ __all__ = [
1311
1349
  "NamedArrayAxes",
1312
1350
  "NamedArrayAxesSpec",
1313
1351
  "Named",
1352
+ "set_mesh",
1314
1353
  ]
@@ -197,7 +197,7 @@ def from_state_dict(tree: T, state_dict: StateDict, prefix: Optional[str] = None
197
197
  array = state_dict[prefix]
198
198
 
199
199
  if isinstance(array, np.ndarray):
200
- mesh = partitioning._get_mesh()
200
+ mesh = jax.sharding.get_abstract_mesh()
201
201
  # TODO: modernize this
202
202
  if jax.device_count() > 1: # this happens with the default mesh
203
203
  pspec = partitioning.pspec_for_axis(tree.axes)
@@ -623,8 +623,8 @@ class NamedArray(metaclass=NamedArrayMeta):
623
623
  def argmin(self, axis: Optional[AxisSelector]) -> "NamedArray": # pragma: no cover
624
624
  return haliax.argmin(self, axis=axis)
625
625
 
626
- def argsort(self, axis: AxisSelector) -> "NamedArray": # pragma: no cover
627
- return haliax.argsort(self, axis=axis)
626
+ def argsort(self, axis: AxisSelector | None, *, stable: bool = False) -> "NamedArray": # pragma: no cover
627
+ return haliax.argsort(self, axis=axis, stable=stable)
628
628
 
629
629
  def astype(self, dtype) -> "NamedArray": # pragma: no cover
630
630
  return NamedArray(self.array.astype(dtype), self.axes)
@@ -1183,6 +1183,26 @@ def index(array: NamedArray, slices: Mapping[AxisSelector, NamedIndex]) -> Named
1183
1183
  def _compute_new_axes_and_slices_for_index(
1184
1184
  array, slices
1185
1185
  ) -> tuple[AxisSpec, list[py_slice | dslice | jnp.ndarray | int | list[int]]]:
1186
+ def _is_integer_like_scalar_index(value: Any) -> bool:
1187
+ if isinstance(value, (int, np.integer)):
1188
+ return True
1189
+ if not is_jax_array_like(value):
1190
+ return False
1191
+ shape = getattr(value, "shape", None)
1192
+ if shape != ():
1193
+ return False
1194
+ dtype = getattr(value, "dtype", None)
1195
+ if dtype is None:
1196
+ return False
1197
+ return jnp.issubdtype(dtype, jnp.integer)
1198
+
1199
+ def _coerce_integer_like_index(value: Any):
1200
+ if isinstance(value, np.integer):
1201
+ return int(value)
1202
+ if type(value) is np.ndarray and value.shape == () and jnp.issubdtype(value.dtype, jnp.integer):
1203
+ return int(value.item())
1204
+ return value
1205
+
1186
1206
  ordered_slices: list = [py_slice(None, None, None)] * len(array.axes) # type: ignore
1187
1207
  kept_axes = [True] * len(array.axes)
1188
1208
  array_slice_indices = []
@@ -1205,8 +1225,8 @@ def _compute_new_axes_and_slices_for_index(
1205
1225
  kept_axes[axis_index] = False
1206
1226
  array_slice_indices.append(axis_index)
1207
1227
  index_axis_names.add(orig_axis.name)
1208
- elif isinstance(slice_, int):
1209
- ordered_slices[axis_index] = slice_
1228
+ elif _is_integer_like_scalar_index(slice_):
1229
+ ordered_slices[axis_index] = _coerce_integer_like_index(slice_)
1210
1230
  kept_axes[axis_index] = False
1211
1231
  elif isinstance(slice_, NamedArray):
1212
1232
  ordered_slices[axis_index] = slice_
@@ -1,3 +1,7 @@
1
+ # Copyright 2025 The Levanter Authors
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
1
5
  """Named wrappers around :mod:`jax.numpy.fft`.
2
6
 
3
7
  These functions mirror the behaviour of their :mod:`jax.numpy.fft` counterparts
@@ -43,7 +47,6 @@ import jax.numpy.fft as jfft
43
47
  from .axis import Axis, AxisSelector, AxisSelection
44
48
  from .core import NamedArray
45
49
 
46
-
47
50
  AxisSizeLike = int | Axis | None
48
51
  AxisMapping = Mapping[AxisSelector, AxisSizeLike]
49
52
 
@@ -3,6 +3,8 @@
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
5
 
6
+ import numbers
7
+
6
8
  import jax.nn as jnn
7
9
  import jax.numpy as jnp
8
10
 
@@ -64,16 +66,25 @@ def one_hot(x: NamedArray | int, class_axis: Axis, *, dtype=None) -> NamedArray:
64
66
  # Disabling this to prevent a crash in XLA on GPU
65
67
  # return hax.auto_sharded(hax.named(array, x.axes + (class_axis,)))
66
68
  return hax.named(array, x.axes + (class_axis,))
67
- else:
68
- assert isinstance(x, int)
69
+
70
+ x_array = jnp.asarray(x)
71
+ if x_array.ndim != 0:
72
+ raise TypeError("one_hot expects a scalar integer or NamedArray, " f"but got array with shape {x_array.shape}")
73
+
74
+ if isinstance(x, numbers.Integral):
69
75
  assert class_axis.size > x >= -class_axis.size
76
+ else:
77
+ if not (jnp.issubdtype(x_array.dtype, jnp.integer) or jnp.issubdtype(x_array.dtype, jnp.bool_)):
78
+ raise TypeError(
79
+ "one_hot expects an integer scalar or NamedArray, " f"but got value with dtype {x_array.dtype}"
80
+ )
70
81
 
71
- one = 1
72
- if dtype is not None:
73
- one = dtype(one)
82
+ one = 1
83
+ if dtype is not None:
84
+ one = dtype(one)
74
85
 
75
- array = jnp.zeros(class_axis.size, dtype=dtype).at[x].set(one)
76
- return hax.auto_sharded(haliax.named(array, class_axis))
86
+ array = jnp.zeros(class_axis.size, dtype=dtype).at[x_array].set(one)
87
+ return hax.auto_sharded(haliax.named(array, class_axis))
77
88
 
78
89
 
79
90
  __all__ = [
@@ -251,7 +251,7 @@ def _gmm(lhs, rhs, group_sizes, out_axes, sharded=False, ar=False):
251
251
  else:
252
252
  gmm_fn = shard_map(
253
253
  partial(gmm_sharded, ar=ar),
254
- mesh=hax.partitioning._get_mesh(),
254
+ mesh=jax.sharding.get_abstract_mesh(),
255
255
  in_specs=(
256
256
  hax.partitioning.pspec_for_axis(lhs.axes),
257
257
  hax.partitioning.pspec_for_axis(rhs.axes),
@@ -127,9 +127,7 @@ def nonzero(array: NamedArray, *, size: Axis, fill_value: int = 0) -> tuple[Name
127
127
  if not isinstance(array, NamedArray):
128
128
  raise ValueError("array must be a NamedArray")
129
129
 
130
- return tuple(
131
- NamedArray(idx, (size,)) for idx in jnp.nonzero(array.array, size=size.size, fill_value=fill_value)
132
- )
130
+ return tuple(NamedArray(idx, (size,)) for idx in jnp.nonzero(array.array, size=size.size, fill_value=fill_value))
133
131
 
134
132
 
135
133
  def clip(array: NamedOrNumeric, a_min: NamedOrNumeric, a_max: NamedOrNumeric) -> NamedArray:
@@ -335,7 +333,7 @@ def unique(
335
333
  )
336
334
 
337
335
  if axis is not None:
338
- axis_index = array._lookup_indices(axis)
336
+ axis_index = array.axis_indices(axis)
339
337
  if axis_index is None:
340
338
  raise ValueError(f"Axis {axis} not found in array. Available axes: {array.axes}")
341
339
  out = jnp.unique(array.array, axis=axis_index, **kwargs)
@@ -10,31 +10,13 @@ import threading
10
10
  import typing
11
11
  import warnings
12
12
  from math import prod
13
- from typing import Callable, ContextManager, Mapping, Optional, ParamSpec, Sequence, TypeVar, Union
13
+ from typing import Callable, ContextManager, Mapping, Optional, ParamSpec, Sequence, TypeVar, Union, cast
14
14
 
15
15
  import equinox as eqx
16
16
  import jax
17
17
  from equinox import is_array, module_update_wrapper
18
18
  from jax.lax import with_sharding_constraint
19
- from jax.sharding import (
20
- Mesh,
21
- NamedSharding,
22
- PartitionSpec,
23
- SingleDeviceSharding,
24
- )
25
-
26
- try: # jax>=0.4.26
27
- from jax.sharding import AbstractMesh, get_abstract_mesh
28
- except Exception: # pragma: no cover - older JAX versions
29
- AbstractMesh = Mesh # type: ignore[misc,assignment]
30
-
31
- def get_abstract_mesh(): # type: ignore[dead-code]
32
- try:
33
- from jax.interpreters.pxla import thread_resources
34
- except Exception:
35
- from jax.experimental.maps import thread_resources
36
-
37
- return thread_resources.env.physical_mesh
19
+ from jax.sharding import AbstractMesh, NamedSharding, Mesh, PartitionSpec, SingleDeviceSharding, get_abstract_mesh
38
20
 
39
21
 
40
22
  from jaxtyping import PyTree
@@ -50,6 +32,7 @@ from .util import StringHolderEnum
50
32
 
51
33
  PhysicalAxisSpec = Union[(str), Sequence[str]]
52
34
  ResourceMapping = Mapping[(str), PhysicalAxisSpec]
35
+ MeshLike = Union[Mesh, AbstractMesh]
53
36
  """Mapping from logical axis names to physical axis names"""
54
37
 
55
38
  F = typing.TypeVar("F", bound=typing.Callable)
@@ -109,6 +92,71 @@ def current_thread_local_mapping():
109
92
  return _mapping_holder.thread_data.resource_mapping
110
93
 
111
94
 
95
+ def _resolve_mesh(mesh: Optional[MeshLike] = None) -> Optional[MeshLike]:
96
+ """Inside jit, prefer an abstract mesh, outside jit prefer a concrete mesh."""
97
+
98
+ from jax._src.mesh import get_concrete_mesh
99
+
100
+ if mesh is not None:
101
+ if is_in_jit() and isinstance(mesh, Mesh):
102
+ return mesh.abstract_mesh
103
+ return mesh
104
+
105
+ if is_in_jit():
106
+ abstract = get_abstract_mesh()
107
+ if not abstract or abstract.empty:
108
+ concrete = get_concrete_mesh()
109
+ if concrete is not None and not concrete.empty:
110
+ return concrete.abstract_mesh
111
+
112
+ from jax.interpreters.pxla import thread_resources
113
+
114
+ old_mesh = thread_resources.env.physical_mesh
115
+ if old_mesh is not None and not old_mesh.empty:
116
+ return old_mesh.abstract_mesh
117
+
118
+ return abstract
119
+ else:
120
+ mesh = get_concrete_mesh() or get_abstract_mesh()
121
+ if mesh is not None and not mesh.empty:
122
+ return mesh
123
+
124
+ from jax.interpreters.pxla import thread_resources
125
+
126
+ old_mesh = thread_resources.env.physical_mesh
127
+ if old_mesh is not None and not old_mesh.empty:
128
+ return old_mesh
129
+
130
+ return None
131
+
132
+
133
+ def mesh_context(mesh: MeshLike) -> ContextManager[None]:
134
+ """Context manager that normalizes mesh handling across JAX versions."""
135
+
136
+ set_mesh_fn = getattr(jax, "set_mesh", None)
137
+ use_mesh_fn = getattr(jax.sharding, "use_mesh", None)
138
+
139
+ manager_factory: Optional[Callable[[MeshLike], ContextManager[None]]] = None
140
+ if set_mesh_fn is not None:
141
+ manager_factory = cast(Callable[[MeshLike], ContextManager[None]], set_mesh_fn)
142
+ elif use_mesh_fn is not None:
143
+ manager_factory = cast(Callable[[MeshLike], ContextManager[None]], use_mesh_fn)
144
+
145
+ if manager_factory is None:
146
+ msg = "Haliax requires a version of JAX that provides either `jax.set_mesh` or `jax.sharding.use_mesh`."
147
+ raise RuntimeError(msg)
148
+
149
+ context_manager = manager_factory(mesh)
150
+
151
+ return context_manager
152
+
153
+
154
+ def set_mesh(mesh: MeshLike) -> ContextManager[None]:
155
+ """Compatibility wrapper around `mesh_context` matching the JAX 0.7 API."""
156
+
157
+ return mesh_context(mesh)
158
+
159
+
112
160
  def auto_sharded(x: T, mesh: Optional[Mesh] = None) -> T:
113
161
  """
114
162
  Shard a PyTree using the global axis mapping. NamedArrays in the PyTree are sharded using the axis mapping
@@ -143,11 +191,15 @@ def shard(x: T, mapping: Optional[ResourceMapping] = None, mesh: Optional[Mesh]
143
191
 
144
192
  assert not isinstance(mesh, dict)
145
193
 
146
- if mesh is None:
147
- mesh = _get_mesh()
194
+ resolved_mesh = _resolve_mesh(mesh)
148
195
 
149
- if mesh.empty:
150
- return x
196
+ if resolved_mesh is None:
197
+ if not is_in_jit():
198
+ warnings.warn("No mesh found. Not sharding.", RuntimeWarning)
199
+ return x
200
+
201
+ if isinstance(resolved_mesh, AbstractMesh) and resolved_mesh.empty:
202
+ return x
151
203
 
152
204
  if is_in_jit() and is_on_mac_metal():
153
205
  warnings.warn("Sharding constraints are not supported in jit on metal", RuntimeWarning)
@@ -162,16 +214,11 @@ def shard(x: T, mapping: Optional[ResourceMapping] = None, mesh: Optional[Mesh]
162
214
  # could use eqx.partition to avoid this, but eh
163
215
  return named
164
216
 
165
- sharding = infer_resource_partitions(named, mapping, mesh=mesh, preserve_existing_shardings=False)
166
- assert isinstance(sharding, NamedSharding)
167
- in_sharding = getattr(named.array, "sharding", None)
217
+ pspec = pspec_for(named, mapping, preserve_existing_shardings=False)
218
+ assert isinstance(pspec, PartitionSpec)
219
+ sharding = NamedSharding(resolved_mesh, pspec)
168
220
  if is_in_jit():
169
221
  return with_sharding_constraint(named, sharding)
170
- # as a special case, SingleDeviceShardings are routed through jit
171
- elif isinstance(in_sharding, SingleDeviceSharding) and in_sharding._device in sharding.device_set:
172
- # TODO(dlwh): this should be unnecessary in JAX soon. Check after 2024-08-01
173
- sharded_array = jax.jit(lambda x: x, out_shardings=sharding)(named)
174
- return sharded_array
175
222
  else:
176
223
  ret = jax.device_put(named, sharding)
177
224
  return ret
@@ -189,7 +236,6 @@ def pspec_for(
189
236
  tree: PyTree,
190
237
  resource_mapping: Optional[ResourceMapping] = None,
191
238
  preserve_existing_shardings: bool = True,
192
- use_auto_sharding: bool = True,
193
239
  ) -> PyTree:
194
240
  """Infer the :class:`PartitionSpec` for a module.
195
241
 
@@ -276,7 +322,6 @@ def infer_resource_partitions(
276
322
  tree: PyTree,
277
323
  resource_mapping: Optional[ResourceMapping] = None,
278
324
  preserve_existing_shardings: bool = True,
279
- use_auto_sharding: bool = True,
280
325
  mesh: Optional[Mesh] = None,
281
326
  ) -> PyTree:
282
327
  """
@@ -291,11 +336,12 @@ def infer_resource_partitions(
291
336
  tree,
292
337
  resource_mapping=resource_mapping,
293
338
  preserve_existing_shardings=preserve_existing_shardings,
294
- use_auto_sharding=use_auto_sharding,
295
339
  )
296
340
 
297
- mesh = mesh or _get_mesh()
298
- assert not isinstance(mesh, dict)
341
+ resolved_mesh = _resolve_mesh(mesh)
342
+ if resolved_mesh is None:
343
+ raise ValueError("No mesh found")
344
+ assert not isinstance(resolved_mesh, dict)
299
345
 
300
346
  def to_sharding(node: typing.Any, spec: typing.Any):
301
347
  if spec is None:
@@ -306,7 +352,7 @@ def infer_resource_partitions(
306
352
  else:
307
353
  return None
308
354
  else:
309
- return NamedSharding(mesh, spec)
355
+ return NamedSharding(resolved_mesh, spec)
310
356
 
311
357
  return htu.tree_map(to_sharding, tree, pspecs)
312
358
 
@@ -407,7 +453,7 @@ class _NamedJitWrapper(eqx.Module):
407
453
  if out_axis_resources is not None:
408
454
  # TODO: when AUTO is fixed (or eval_shape can give shardings), use it here
409
455
  out_resources = infer_resource_partitions(
410
- output_shape, out_axis_resources, preserve_existing_shardings=False, use_auto_sharding=False
456
+ output_shape, out_axis_resources, preserve_existing_shardings=False
411
457
  )
412
458
  my_pjit_args["out_shardings"] = out_resources
413
459
 
@@ -642,7 +688,7 @@ def physical_axis_name(axis: AxisSelector, mapping: Optional[ResourceMapping] =
642
688
  def physical_axis_size(axis: AxisSelector, mapping: Optional[ResourceMapping] = None) -> Optional[int]:
643
689
  """Get the physical axis size for a logical axis. This is the product of the size of all physical axes
644
690
  that this logical axis is mapped to."""
645
- mesh = _get_mesh()
691
+ mesh = _resolve_mesh()
646
692
 
647
693
  if mesh is None:
648
694
  raise ValueError("No mesh found")
@@ -659,10 +705,14 @@ def physical_axis_size(axis: AxisSelector, mapping: Optional[ResourceMapping] =
659
705
 
660
706
 
661
707
  def sharding_for_axis(
662
- axis: AxisSelection, mapping: Optional[ResourceMapping] = None, mesh: Optional[Mesh] = None
708
+ axis: AxisSelection, mapping: Optional[ResourceMapping] = None, mesh: Optional[MeshLike] = None
663
709
  ) -> NamedSharding:
664
710
  """Get the sharding for a single axis"""
665
- return NamedSharding(mesh or _get_mesh(), pspec_for_axis(axis, mapping))
711
+ resolved_mesh = _resolve_mesh(mesh)
712
+ if resolved_mesh is None:
713
+ raise ValueError("No mesh found")
714
+
715
+ return NamedSharding(resolved_mesh, pspec_for_axis(axis, mapping))
666
716
 
667
717
 
668
718
  def pspec_for_axis(axis: AxisSelection, mapping: Optional[ResourceMapping] = None) -> PartitionSpec:
@@ -681,28 +731,17 @@ def round_axis_for_partitioning(axis: Axis, mapping: Optional[ResourceMapping] =
681
731
  return Axis(axis.name, new_size)
682
732
 
683
733
 
684
- def _get_mesh() -> Mesh | AbstractMesh:
685
- """Return the current mesh.
686
-
687
- On newer versions of JAX this prefers ``get_abstract_mesh`` which does not
688
- capture concrete devices. If no abstract mesh is currently active we fall
689
- back to the concrete mesh used by ``Mesh``'s context manager so existing
690
- code continues to work.
691
- """
734
+ def _get_mesh() -> Mesh | None:
735
+ """Deprecated helper that simply proxies to :func:`get_abstract_mesh`."""
692
736
 
693
- try: # jax>=0.4.26
694
- mesh = get_abstract_mesh()
695
- if not getattr(mesh, "empty", False):
696
- return mesh
697
- except Exception: # pragma: no cover - older JAX versions
698
- pass
699
-
700
- try:
701
- from jax.interpreters.pxla import thread_resources
702
- except Exception: # pragma: no cover - jax<0.4
703
- from jax.experimental.maps import thread_resources
737
+ warnings.warn(
738
+ "`_get_mesh` is deprecated; use `jax's get_abstract_mesh or get_concrete_mesh` instead",
739
+ DeprecationWarning,
740
+ stacklevel=2,
741
+ )
704
742
 
705
- return thread_resources.env.physical_mesh
743
+ mesh = _resolve_mesh()
744
+ return mesh
706
745
 
707
746
 
708
747
  def _is_jit_tracer(x) -> bool: