haliax 1.4.dev411__tar.gz → 1.4.dev413__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 (128) hide show
  1. {haliax-1.4.dev411 → haliax-1.4.dev413}/.agents/projects/api_parity.md +13 -18
  2. {haliax-1.4.dev411 → haliax-1.4.dev413}/.playbooks/wrap-non-named.md +8 -0
  3. {haliax-1.4.dev411 → haliax-1.4.dev413}/PKG-INFO +1 -1
  4. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/api.md +124 -11
  5. haliax-1.4.dev413/docs/primer.md +183 -0
  6. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/__about__.py +1 -1
  7. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/__init__.py +22 -0
  8. haliax-1.4.dev413/src/haliax/fft.py +365 -0
  9. haliax-1.4.dev413/tests/test_fft.py +81 -0
  10. haliax-1.4.dev411/docs/primer.md +0 -114
  11. {haliax-1.4.dev411 → haliax-1.4.dev413}/.coveragerc +0 -0
  12. {haliax-1.4.dev411 → haliax-1.4.dev413}/.flake8 +0 -0
  13. {haliax-1.4.dev411 → haliax-1.4.dev413}/.github/workflows/publish_dev.yaml +0 -0
  14. {haliax-1.4.dev411 → haliax-1.4.dev413}/.github/workflows/run_pre_commit.yaml +0 -0
  15. {haliax-1.4.dev411 → haliax-1.4.dev413}/.github/workflows/run_quick_levanter_tests.yaml +0 -0
  16. {haliax-1.4.dev411 → haliax-1.4.dev413}/.github/workflows/run_tests.yaml +0 -0
  17. {haliax-1.4.dev411 → haliax-1.4.dev413}/.gitignore +0 -0
  18. {haliax-1.4.dev411 → haliax-1.4.dev413}/.playbooks/add-types.md +0 -0
  19. {haliax-1.4.dev411 → haliax-1.4.dev413}/.pre-commit-config.yaml +0 -0
  20. {haliax-1.4.dev411 → haliax-1.4.dev413}/.readthedocs.yaml +0 -0
  21. {haliax-1.4.dev411 → haliax-1.4.dev413}/AGENTS.md +0 -0
  22. {haliax-1.4.dev411 → haliax-1.4.dev413}/AUTHORS.md +0 -0
  23. {haliax-1.4.dev411 → haliax-1.4.dev413}/CONTRIBUTING.md +0 -0
  24. {haliax-1.4.dev411 → haliax-1.4.dev413}/CONTRIBUTORS.md +0 -0
  25. {haliax-1.4.dev411 → haliax-1.4.dev413}/LICENSE +0 -0
  26. {haliax-1.4.dev411 → haliax-1.4.dev413}/README.md +0 -0
  27. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/broadcasting.md +0 -0
  28. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/cheatsheet.md +0 -0
  29. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/css/material.css +0 -0
  30. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/css/mkdocstrings.css +0 -0
  31. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/faq.md +0 -0
  32. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/figures/data_parallel_mesh.png +0 -0
  33. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/figures/data_parallel_mesh_replicated.png +0 -0
  34. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/figures/device_mesh_1d.png +0 -0
  35. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/figures/device_mesh_1d_zero.png +0 -0
  36. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/figures/device_mesh_2d.png +0 -0
  37. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/figures/device_mesh_2d_batch_partitioned.png +0 -0
  38. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/figures/device_mesh_2d_data_replicated.png +0 -0
  39. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/figures/device_mesh_2d_data_replicated_mlp_partitioned.png +0 -0
  40. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/figures/device_mesh_2d_intermediate_fully_partitioned.png +0 -0
  41. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/figures/device_mesh_2d_zero.png +0 -0
  42. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/fp8.md +0 -0
  43. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/index.md +0 -0
  44. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/indexing.md +0 -0
  45. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/matmul.md +0 -0
  46. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/nn.md +0 -0
  47. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/partitioning.md +0 -0
  48. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/rearrange.ipynb +0 -0
  49. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/rearrange.md +0 -0
  50. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/requirements.txt +0 -0
  51. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/scan.md +0 -0
  52. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/state-dict.md +0 -0
  53. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/tutorial.md +0 -0
  54. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/typing.md +0 -0
  55. {haliax-1.4.dev411 → haliax-1.4.dev413}/docs/vmap.md +0 -0
  56. {haliax-1.4.dev411 → haliax-1.4.dev413}/etc/license_header.txt +0 -0
  57. {haliax-1.4.dev411 → haliax-1.4.dev413}/mkdocs.yml +0 -0
  58. {haliax-1.4.dev411 → haliax-1.4.dev413}/pyproject.toml +0 -0
  59. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/_src/__init__.py +0 -0
  60. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/_src/compile_utils.py +0 -0
  61. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/_src/dot.py +0 -0
  62. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/_src/einsum.py +0 -0
  63. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/_src/fp8.py +0 -0
  64. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/_src/parsing.py +0 -0
  65. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/_src/rearrange.py +0 -0
  66. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/_src/scan.py +0 -0
  67. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/_src/state_dict.py +0 -0
  68. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/_src/util.py +0 -0
  69. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/axis.py +0 -0
  70. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/core.py +0 -0
  71. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/debug.py +0 -0
  72. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/field.py +0 -0
  73. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/haxtyping.py +0 -0
  74. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/hof.py +0 -0
  75. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/jax_utils.py +0 -0
  76. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/nn/__init__.py +0 -0
  77. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/nn/activations.py +0 -0
  78. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/nn/attention.py +0 -0
  79. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/nn/conv.py +0 -0
  80. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/nn/dropout.py +0 -0
  81. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/nn/embedding.py +0 -0
  82. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/nn/linear.py +0 -0
  83. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/nn/loss.py +0 -0
  84. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/nn/mlp.py +0 -0
  85. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/nn/normalization.py +0 -0
  86. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/nn/pool.py +0 -0
  87. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/nn/scan.py +0 -0
  88. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/ops.py +0 -0
  89. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/partitioning.py +0 -0
  90. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/quantization.py +0 -0
  91. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/random.py +0 -0
  92. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/specialized_fns.py +0 -0
  93. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/state_dict.py +0 -0
  94. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/tree_util.py +0 -0
  95. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/types.py +0 -0
  96. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/util.py +0 -0
  97. {haliax-1.4.dev411 → haliax-1.4.dev413}/src/haliax/wrap.py +0 -0
  98. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/core_test.py +0 -0
  99. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_attention.py +0 -0
  100. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_axis.py +0 -0
  101. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_bitwise_ops.py +0 -0
  102. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_conv.py +0 -0
  103. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_debug.py +0 -0
  104. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_dot.py +0 -0
  105. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_dtype_typing.py +0 -0
  106. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_einsum.py +0 -0
  107. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_field.py +0 -0
  108. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_fp8.py +0 -0
  109. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_hof.py +0 -0
  110. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_int8.py +0 -0
  111. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_moe_linear.py +0 -0
  112. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_namedarray_typing.py +0 -0
  113. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_nan_reductions.py +0 -0
  114. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_nn.py +0 -0
  115. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_ops.py +0 -0
  116. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_parsing.py +0 -0
  117. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_partitioning.py +0 -0
  118. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_pool.py +0 -0
  119. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_random.py +0 -0
  120. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_rearrange.py +0 -0
  121. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_scan.py +0 -0
  122. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_scatter_gather.py +0 -0
  123. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_specialized_fns.py +0 -0
  124. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_state_dict.py +0 -0
  125. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_tree_util.py +0 -0
  126. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_utils.py +0 -0
  127. {haliax-1.4.dev411 → haliax-1.4.dev413}/tests/test_visualize_sharding.py +0 -0
  128. {haliax-1.4.dev411 → haliax-1.4.dev413}/uv.lock +0 -0
@@ -161,24 +161,19 @@ APIs that don't translate well to named tensors are intentionally omitted here.
161
161
  - [ ] `vstack`
162
162
 
163
163
  ## fft
164
- - [ ] `fft`
165
- - [ ] `fft2`
166
- - [ ] `fftfreq`
167
- - [ ] `fftn`
168
- - [ ] `fftshift`
169
- - [ ] `hfft`
170
- - [ ] `ifft`
171
- - [ ] `ifft2`
172
- - [ ] `ifftn`
173
- - [ ] `ifftshift`
174
- - [ ] `ihfft`
175
- - [ ] `irfft`
176
- - [ ] `irfft2`
177
- - [ ] `irfftn`
178
- - [ ] `rfft`
179
- - [ ] `rfft2`
180
- - [ ] `rfftfreq`
181
- - [ ] `rfftn`
164
+ Multi-dimensional transforms like `fftn` or `rfft2` are handled by passing a
165
+ mapping of axes to :func:`haliax.fft` and friends.
166
+
167
+ - [x] `fft`
168
+ - [x] `fftfreq`
169
+ - [x] `fftshift`
170
+ - [x] `hfft`
171
+ - [x] `ifft`
172
+ - [x] `ifftshift`
173
+ - [x] `ihfft`
174
+ - [x] `irfft`
175
+ - [x] `rfft`
176
+ - [x] `rfftfreq`
182
177
 
183
178
  ## linalg
184
179
  - [ ] `cholesky`
@@ -45,6 +45,14 @@ def sum(a, axis=None):
45
45
  ## Harder Cases
46
46
  Some functions need bespoke handling. For example `jnp.unique` returns several arrays and may change shape unpredictably. There is no generic helper, so you will need to manually map between `NamedArray` axes and the outputs. Use the lower level utilities in `haliax.wrap` for broadcasting and axis lookup.
47
47
 
48
+ ## Axis-aware functions
49
+ For JAX functions that accept an ``axis`` or ``axes`` argument, prefer a single
50
+ ``axis`` parameter of type :class:`haliax.AxisSelection` that also accepts an
51
+ ordered mapping of axes to sizes. A mapping dispatches to the corresponding
52
+ ``n``‑dimensional JAX primitive (e.g. :func:`jax.numpy.fft.fftn`) and allows
53
+ resizing the transformed axes by specifying integer lengths or ``Axis``
54
+ instances.
55
+
48
56
  ## Testing
49
57
  Add tests to ensure that named and unnamed calls produce the same results and that axis names are preserved or removed correctly.
50
58
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: haliax
3
- Version: 1.4.dev411
3
+ Version: 1.4.dev413
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/
@@ -6,19 +6,89 @@ arrays (see [haliax.zeros][] and [haliax.ones][]) as well as things like reducti
6
6
 
7
7
  ## Axis Types
8
8
 
9
- There are four types related to [haliax.Axis][] you will see in the API reference:
9
+ If you already speak NumPy or `jax.numpy`, think of Haliax as swapping positional axes (`axis=0`) for named axes
10
+ (`axis="batch"`). The type hints in this section describe the different ways those named axes can be provided to the API.
11
+ They appear throughout the documentation and in signatures so that you can quickly tell which forms are accepted.
10
12
 
11
- * [haliax.Axis][]: This is the main type for representing axes. It is a dataclass with a name and a size.
12
- * [haliax.AxisSelector][]: This is a type alias for either [haliax.Axis][] or a `str`. This type is used when we want
13
- one axis and the size can be inferred from the inputs.
14
- * [haliax.AxisSpec][]: This is a type alias for either [haliax.Axis][] or a tuple/list of [haliax.Axis][]. This type is
15
- used when we want one or more axes and the sizes cannot be inferred from the inputs, for instance when creating arrays.
16
- * [haliax.AxisSelection][]: This is a type alias for either [haliax.AxisSelector][] or a tuple/list of [haliax.AxisSelector][].
17
- This type is used when we want one or more axes and the sizes can be inferred from the inputs, for instance when
18
- reducing an array.
13
+ | Name | Accepts | When to use it | Example |
14
+ | --- | --- | --- | --- |
15
+ | [`Axis`][haliax.Axis] | `Axis(name: str, size: int)` | Define a named dimension with an explicit size | `Batch = Axis("batch", 32)` |
16
+ | [`AxisSelector`][haliax.AxisSelector] | `Axis` or `str` | Refer to an existing axis whose size can be inferred from the arrays you pass in | `x.sum(axis="batch")` |
17
+ | [`AxisSpec`][haliax.AxisSpec] | `dict[str, int]`, `Axis`, or a sequence of `Axis` objects | Create or reshape arrays when the axis sizes must be provided | `hax.zeros((Batch, Feature))` |
18
+ | [`AxisSelection`][haliax.AxisSelection] | `dict[str, int | None]`, `AxisSpec`, or a sequence of `AxisSelector` values | Work with one or more existing axes (reductions, indexing helpers, flattening, …) | `x.sum(axis=("batch", Feature))` |
19
19
 
20
- Occasionally, an axis size can be inferred in some circumstances but not others. When this happens, we still use
21
- `AxisSelector` but document the behavior in the docstring. A RuntimeError will be raised if the size cannot be inferred.
20
+ ### Axis
21
+
22
+ An [`Axis`][haliax.Axis] is the fundamental building block: it is a tiny dataclass that stores a name and a size. You can
23
+ construct one directly or use [`haliax.make_axes`][] to generate several at a time.
24
+
25
+ ```python
26
+ import haliax as hax
27
+ from haliax import Axis
28
+
29
+ Batch = Axis("batch", 32)
30
+ Feature = Axis("feature", 128)
31
+ x = hax.ones((Batch, Feature))
32
+ print(Batch.name, Batch.size)
33
+ ```
34
+
35
+ Using `Axis` objects keeps array creation explicit and gives reusable handles you can share between different tensors.
36
+ Equality compares both the name and size so you get guardrails when wiring pieces together.
37
+
38
+ ### AxisSelector
39
+
40
+ An [`AxisSelector`][haliax.AxisSelector] accepts either an `Axis` object or just the axis name as a string. It is used
41
+ whenever a function can read the axis size from one of its arguments. This mirrors how NumPy lets you pass `axis=0` when
42
+ reducing an array:
43
+
44
+ ```python
45
+ total = x.sum(axis=Batch) # using the Axis handle
46
+ same_total = x.sum(axis="batch") # using only the name
47
+ ```
48
+
49
+ Strings are convenient when you only care about the name, but `Axis` objects still work so you can keep using the handles
50
+ you created earlier. If an axis with that name is missing, Haliax raises a `ValueError`.
51
+
52
+ ### AxisSpec
53
+
54
+ An [`AxisSpec`][haliax.AxisSpec] is used when Haliax needs full size information to create or reshape an array. You can
55
+ provide a shape dictionary (sometimes called a "shape dict") that maps names to sizes, or a sequence of `Axis` objects:
56
+
57
+ ```python
58
+ shape = {"batch": 32, "feature": 128}
59
+ y = hax.zeros(shape) # using a shape dict
60
+ z = hax.zeros((Batch, Feature)) # using the Axis objects directly
61
+ ```
62
+
63
+ Both forms describe the same layout. Python dictionaries preserve insertion order, so the ordering in a shape dict matches
64
+ the order that axes appear in the array. Sequences must contain `Axis` objects (not plain strings) because Haliax cannot
65
+ otherwise know the axis sizes.
66
+
67
+ ### AxisSelection
68
+
69
+ [`AxisSelection`][haliax.AxisSelection] generalizes the previous aliases so you can talk about several axes at once. It
70
+ shows up in reductions, indexing helpers, axis-mapping utilities, and anywhere you might have written `axis=(0, 1)` in
71
+ NumPy. You may supply:
72
+
73
+ * a sequence mixing `Axis` objects and strings, e.g. `("batch", Feature)` when reducing two axes,
74
+ * an `AxisSpec`, which is handy when you already have a tuple of `Axis` objects, or
75
+ * a "partial shape dict" where the values are either sizes or `None` to indicate "any size". Dictionaries are useful when
76
+ you only care about a subset of axes or want to assert a particular size.
77
+
78
+ ```python
79
+ # Reduce over two axes using a tuple of selectors.
80
+ scalar = x.sum(axis=("batch", Feature))
81
+
82
+ # Ask for the axes by name and optionally pin sizes.
83
+ x.resolve_axis({"batch": None, "feature": None}) # returns {"batch": 32, "feature": 128}
84
+
85
+ from haliax.axis import selects_axis
86
+ assert selects_axis((Batch, "feature"), {"batch": None, "feature": 128})
87
+ ```
88
+
89
+ Occasionally, an axis size can be inferred in some circumstances but not others. When this happens we still use
90
+ `AxisSelector` (or `AxisSelection` for multiple axes) but document the behavior in the docstring. A `RuntimeError` will be
91
+ raised if the size cannot be inferred.
22
92
 
23
93
  ::: haliax.Axis
24
94
  ::: haliax.AxisSelector
@@ -289,6 +359,49 @@ These are all more or less directly from JAX's NumPy API.
289
359
  ::: haliax.triu
290
360
  ::: haliax.where
291
361
 
362
+ ### FFT
363
+
364
+ All FFT helpers accept an ``axis`` argument which may be a single axis, its
365
+ name, or an ordered mapping from axes to output sizes. Passing a mapping
366
+ dispatches to the ``n``‑dimensional variants in :mod:`jax.numpy.fft`.
367
+
368
+ For example::
369
+
370
+ import jax.numpy as jnp
371
+ import haliax as hax
372
+
373
+ T = hax.Axis("time", 8)
374
+ signal = hax.arange(T, dtype=jnp.float32)
375
+
376
+ # operate along a single axis specified by name
377
+ hax.fft(signal, axis="time")
378
+
379
+ # resize by passing an Axis object
380
+ hax.fft(signal, axis=hax.Axis("time", 16))
381
+
382
+ X, Y = hax.make_axes(X=4, Y=6)
383
+ image = hax.arange((X, Y), dtype=jnp.float32)
384
+
385
+ # transform across several axes in order by passing a sequence
386
+ hax.fft(image, axis=("X", "Y"))
387
+
388
+ # selectively resize axes by providing a mapping
389
+ hax.fft(image, axis={"X": None, "Y": hax.Axis("Y", 10)})
390
+
391
+ # mappings can cover just a subset of axes when only partial resizing is needed
392
+ hax.fft(image, axis={"Y": 10})
393
+
394
+ ::: haliax.fft
395
+ ::: haliax.ifft
396
+ ::: haliax.hfft
397
+ ::: haliax.ihfft
398
+ ::: haliax.rfft
399
+ ::: haliax.irfft
400
+ ::: haliax.fftfreq
401
+ ::: haliax.rfftfreq
402
+ ::: haliax.fftshift
403
+ ::: haliax.ifftshift
404
+
292
405
 
293
406
 
294
407
  ## Named Array Reference
@@ -0,0 +1,183 @@
1
+ # Haliax Primer
2
+
3
+ Haliax provides named tensors built on top of JAX. This primer is written for LLM agents and other downstream libraries and collects the core ideas for quick reference.
4
+
5
+ ## Axes and Named Arrays
6
+
7
+ Arrays are indexed by `Axis` objects. You can define them explicitly or generate several with `make_axes`.
8
+ You may also specify shapes with a **shape dict**, mapping axis names to sizes.
9
+
10
+ ```python
11
+ import haliax as hax
12
+ from haliax import Axis
13
+
14
+ Batch = Axis("batch", 4)
15
+ Feature = Axis("feature", 8)
16
+ # or: Batch, Feature = hax.make_axes(batch=4, feature=8)
17
+ # using Axis objects
18
+ x = hax.zeros((Batch, Feature))
19
+ # or using a shape dict
20
+ shape = {"batch": 4, "feature": 8}
21
+ x = hax.zeros(shape)
22
+ ```
23
+
24
+ Most functions accept either axes or shape dicts interchangeably.
25
+
26
+ A tensor with named axes is a [`NamedArray`][haliax.NamedArray]. Elementwise operations mirror `jax.numpy` but accept named axes.
27
+
28
+ ### Ways to Describe Axes
29
+
30
+ If you're used to `axis=0` style code in NumPy or JAX, think of Haliax as swapping those positional indices for names
31
+ like `axis="batch"`. The API hints refer to a few helper aliases; this table shows how they map back to familiar
32
+ concepts:
33
+
34
+ | Name | Accepts | Typical use | Example |
35
+ | --- | --- | --- | --- |
36
+ | [`Axis`][haliax.Axis] | `Axis(name: str, size: int)` | Define a named dimension with a fixed size | `Batch = Axis("batch", 32)` |
37
+ | [`AxisSelector`][haliax.AxisSelector] | `Axis` or `str` | Refer to an existing axis when the size can be inferred from the argument | `x.sum(axis="batch")` |
38
+ | [`AxisSpec`][haliax.AxisSpec] | `dict[str, int]`, `Axis`, or a sequence of `Axis` objects | Supply complete shape information (array creation, reshaping) | `hax.zeros((Batch, Feature))` |
39
+ | [`AxisSelection`][haliax.AxisSelection] | `dict[str, int | None]`, `AxisSpec`, or a sequence of `AxisSelector` values | Work with one or more existing axes (reductions, indexing helpers, flattening, …) | `x.sum(axis=("batch", Feature))` |
40
+
41
+ The following sections expand on each alias with quick references and NumPy-style parallels.
42
+
43
+ #### `Axis`: reusable named dimensions
44
+
45
+ An [`Axis`][haliax.Axis] stores a `name` and a `size`. Create them directly or let
46
+ [`haliax.make_axes`][] build a handful at once. Because axes compare by both name and size, they act as reusable handles and
47
+ catch many wiring mistakes early.
48
+
49
+ ```python
50
+ Batch = Axis("batch", 32)
51
+ Feature = Axis("feature", 128)
52
+ x = hax.ones((Batch, Feature))
53
+ print(Batch.name, Batch.size)
54
+ ```
55
+
56
+ #### `AxisSelector`: when the size is already known
57
+
58
+ Many functions already see the array whose axes you're referencing (e.g. reductions). In those cases you can pass either the
59
+ `Axis` object or simply the axis name as a string. Haliax resolves the name against the array, similar to `axis=0` in NumPy.
60
+
61
+ ```python
62
+ total = x.sum(axis=Batch) # use the Axis handle
63
+ same_total = x.sum(axis="batch") # or just the name
64
+ ```
65
+
66
+ If you reference an axis name that isn't present, Haliax raises a `ValueError`.
67
+
68
+ #### `AxisSpec`: describing complete shapes
69
+
70
+ When Haliax needs explicit sizes—creating arrays, reshaping, broadcasting to a new axis—you provide an [`AxisSpec`][haliax.AxisSpec].
71
+ Shape dictionaries keep things close to standard Python, while sequences require actual `Axis` objects so the sizes stay explicit.
72
+
73
+ ```python
74
+ shape = {"batch": 32, "feature": 128}
75
+ y = hax.zeros(shape) # using a shape dict
76
+ z = hax.zeros((Batch, Feature)) # or a sequence of Axis objects
77
+ ```
78
+
79
+ Python dictionaries preserve insertion order, so the layout in a shape dict matches the order of axes in the resulting array.
80
+
81
+ #### `AxisSelection`: several axes at once
82
+
83
+ [`AxisSelection`][haliax.AxisSelection] is the plural form used by reductions, indexing helpers, and flattening utilities. Supply a
84
+ tuple mixing `Axis` objects and strings, reuse an existing `AxisSpec`, or pass a partial shape dict where values are either
85
+ sizes or `None` for "any size".
86
+
87
+ ```python
88
+ scalar = x.sum(axis=("batch", Feature))
89
+
90
+ # Ask for the axes by name and optionally pin sizes.
91
+ x.resolve_axis({"batch": None, "feature": None}) # returns {"batch": 32, "feature": 128}
92
+ ```
93
+
94
+ Partial shape dicts shine when you only care about a subset of axes or want assertions about their sizes. If the axis size
95
+ cannot be inferred from the provided arguments, Haliax raises a `RuntimeError`.
96
+
97
+ ## Indexing and Broadcasting
98
+
99
+ Use axis names when slicing. Dictionaries are convenient for several axes:
100
+
101
+ ```python
102
+ first = x["batch", 0]
103
+ sub = x["batch", 1:3]
104
+ # or with a dict
105
+ first = x[{"batch": 0}]
106
+ sub = x[{"batch": slice(1, 3)}]
107
+ ```
108
+
109
+ Axes broadcast by matching names. `broadcast_axis` adds a new axis to an array:
110
+
111
+ ```python
112
+ row = hax.arange(Feature)
113
+ outer = row.broadcast_axis(Batch) * hax.arange(Batch)
114
+ ```
115
+
116
+ See [Indexing and Slicing](indexing.md) and [Broadcasting](broadcasting.md) for details.
117
+
118
+ ## Rearranging Axes
119
+
120
+ `rearrange` changes axis order and can merge or split axes using einops‑style syntax. It is useful when interfacing with positional APIs.
121
+
122
+ ```python
123
+ # transpose features and batch
124
+ x_t = hax.rearrange(x, "batch feature -> feature batch")
125
+ ```
126
+
127
+ More examples appear in [Rearrange](rearrange.md).
128
+
129
+ ## Matrix Multiplication
130
+
131
+ `dot` contracts over named axes while preserving order independence.
132
+
133
+ ```python
134
+ Weight = Axis("weight", 8)
135
+ w = hax.ones((Feature, Weight))
136
+ prod = hax.dot(x, w, axis=Feature)
137
+ ```
138
+
139
+ For more complex contractions use [`einsum`][haliax.einsum]. See [Matrix Multiplication](matmul.md).
140
+
141
+ ## Scans and Folds
142
+
143
+ Use [`scan`][haliax.scan] or [`fold`][haliax.fold] to apply a function along an axis with optional gradient checkpointing.
144
+
145
+ ```python
146
+ Time = Axis("time", 10)
147
+ sequence = hax.ones((Time, Feature))
148
+
149
+ def add(prev, cur):
150
+ return prev + cur
151
+
152
+ result = hax.fold(add, Time)(hax.zeros((Feature,)), sequence)
153
+ ```
154
+
155
+ See [Scan and Fold](scan.md) for checkpointing policies and stacked modules.
156
+
157
+ ## Partitioning
158
+
159
+ Arrays and modules can be distributed across devices by mapping named axes to mesh axes:
160
+
161
+ ```python
162
+ with hax.axis_mapping({"batch": "data"}):
163
+ sharded = hax.shard(x)
164
+ ```
165
+
166
+ The [Partitioning](partitioning.md) guide explains how to set up device meshes and shard arrays.
167
+
168
+ ## Typing Support
169
+
170
+ Type annotations use `haliax.haxtyping` which extends `jaxtyping`:
171
+
172
+ ```python
173
+ import haliax.haxtyping as ht
174
+
175
+ def f(t: ht.Float[hax.NamedArray, "batch feature"]):
176
+ ...
177
+ ```
178
+
179
+ See [Typing](typing.md) for matching runtime checks and dtype-aware annotations.
180
+
181
+ ---
182
+
183
+ This primer highlights common patterns. The [cheatsheet](cheatsheet.md) lists many additional conversions from JAX to Haliax.
@@ -3,4 +3,4 @@
3
3
  # SPDX-License-Identifier: Apache-2.0
4
4
 
5
5
 
6
- __version__ = "1.4.dev411"
6
+ __version__ = "1.4.dev413"
@@ -96,6 +96,18 @@ from .ops import (
96
96
  bincount,
97
97
  where,
98
98
  )
99
+ from .fft import (
100
+ fft,
101
+ fftfreq,
102
+ fftshift,
103
+ hfft,
104
+ ifft,
105
+ ifftshift,
106
+ ihfft,
107
+ irfft,
108
+ rfft,
109
+ rfftfreq,
110
+ )
99
111
  from .partitioning import auto_sharded, axis_mapping, fsdp, named_jit, shard, shard_with_axis_mapping
100
112
  from .specialized_fns import top_k
101
113
  from .types import Scalar
@@ -1214,6 +1226,16 @@ __all__ = [
1214
1226
  "clip",
1215
1227
  "tril",
1216
1228
  "triu",
1229
+ "fft",
1230
+ "ifft",
1231
+ "hfft",
1232
+ "ihfft",
1233
+ "rfft",
1234
+ "irfft",
1235
+ "fftfreq",
1236
+ "rfftfreq",
1237
+ "fftshift",
1238
+ "ifftshift",
1217
1239
  "add",
1218
1240
  "arctan2",
1219
1241
  "bitwise_and",