haliax 1.4.dev364__tar.gz → 1.4.dev366__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 (114) hide show
  1. {haliax-1.4.dev364 → haliax-1.4.dev366}/.pre-commit-config.yaml +16 -16
  2. haliax-1.4.dev366/AGENTS.md +79 -0
  3. {haliax-1.4.dev364 → haliax-1.4.dev366}/PKG-INFO +2 -1
  4. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/nn.md +0 -1
  5. haliax-1.4.dev366/docs/typing.md +100 -0
  6. {haliax-1.4.dev364 → haliax-1.4.dev366}/pyproject.toml +7 -1
  7. haliax-1.4.dev366/src/haliax/__about__.py +1 -0
  8. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/__init__.py +16 -16
  9. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/_src/dot.py +0 -2
  10. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/_src/fp8.py +0 -1
  11. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/_src/state_dict.py +3 -4
  12. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/core.py +2 -15
  13. haliax-1.4.dev366/src/haliax/haxtyping.py +138 -0
  14. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/hof.py +1 -1
  15. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/nn/attention.py +0 -1
  16. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/nn/embedding.py +0 -2
  17. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/nn/scan.py +3 -3
  18. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/partitioning.py +1 -1
  19. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/random.py +0 -3
  20. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/wrap.py +0 -3
  21. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_attention.py +0 -1
  22. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_dot.py +1 -1
  23. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_dtype_typing.py +1 -1
  24. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_namedarray_typing.py +7 -7
  25. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_scatter_gather.py +0 -1
  26. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_tree_util.py +0 -1
  27. haliax-1.4.dev364/docs/typing.md +0 -61
  28. haliax-1.4.dev364/src/haliax/__about__.py +0 -1
  29. haliax-1.4.dev364/src/haliax/typing.py +0 -88
  30. {haliax-1.4.dev364 → haliax-1.4.dev366}/.coveragerc +0 -0
  31. {haliax-1.4.dev364 → haliax-1.4.dev366}/.flake8 +0 -0
  32. {haliax-1.4.dev364 → haliax-1.4.dev366}/.github/workflows/publish_dev.yaml +0 -0
  33. {haliax-1.4.dev364 → haliax-1.4.dev366}/.github/workflows/run_pre_commit.yaml +0 -0
  34. {haliax-1.4.dev364 → haliax-1.4.dev366}/.github/workflows/run_quick_levanter_tests.yaml +0 -0
  35. {haliax-1.4.dev364 → haliax-1.4.dev366}/.github/workflows/run_tests.yaml +0 -0
  36. {haliax-1.4.dev364 → haliax-1.4.dev366}/.gitignore +0 -0
  37. {haliax-1.4.dev364 → haliax-1.4.dev366}/.readthedocs.yaml +0 -0
  38. {haliax-1.4.dev364 → haliax-1.4.dev366}/CONTRIBUTING.md +0 -0
  39. {haliax-1.4.dev364 → haliax-1.4.dev366}/LICENSE +0 -0
  40. {haliax-1.4.dev364 → haliax-1.4.dev366}/README.md +0 -0
  41. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/api.md +0 -0
  42. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/broadcasting.md +0 -0
  43. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/cheatsheet.md +0 -0
  44. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/css/material.css +0 -0
  45. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/css/mkdocstrings.css +0 -0
  46. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/faq.md +0 -0
  47. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/figures/data_parallel_mesh.png +0 -0
  48. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/figures/data_parallel_mesh_replicated.png +0 -0
  49. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/figures/device_mesh_1d.png +0 -0
  50. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/figures/device_mesh_1d_zero.png +0 -0
  51. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/figures/device_mesh_2d.png +0 -0
  52. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/figures/device_mesh_2d_batch_partitioned.png +0 -0
  53. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/figures/device_mesh_2d_data_replicated.png +0 -0
  54. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/figures/device_mesh_2d_data_replicated_mlp_partitioned.png +0 -0
  55. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/figures/device_mesh_2d_intermediate_fully_partitioned.png +0 -0
  56. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/figures/device_mesh_2d_zero.png +0 -0
  57. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/fp8.md +0 -0
  58. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/index.md +0 -0
  59. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/indexing.md +0 -0
  60. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/matmul.md +0 -0
  61. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/partitioning.md +0 -0
  62. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/rearrange.ipynb +0 -0
  63. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/rearrange.md +0 -0
  64. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/requirements.txt +0 -0
  65. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/scan.md +0 -0
  66. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/state-dict.md +1 -1
  67. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/tutorial.md +0 -0
  68. {haliax-1.4.dev364 → haliax-1.4.dev366}/docs/vmap.md +0 -0
  69. {haliax-1.4.dev364 → haliax-1.4.dev366}/mkdocs.yml +0 -0
  70. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/_src/__init__.py +0 -0
  71. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/_src/compile_utils.py +0 -0
  72. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/_src/einsum.py +0 -0
  73. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/_src/parsing.py +0 -0
  74. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/_src/rearrange.py +0 -0
  75. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/_src/scan.py +0 -0
  76. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/_src/util.py +0 -0
  77. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/axis.py +0 -0
  78. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/debug.py +0 -0
  79. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/jax_utils.py +0 -0
  80. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/nn/__init__.py +0 -0
  81. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/nn/activations.py +0 -0
  82. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/nn/conv.py +0 -0
  83. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/nn/dropout.py +0 -0
  84. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/nn/linear.py +0 -0
  85. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/nn/loss.py +0 -0
  86. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/nn/mlp.py +0 -0
  87. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/nn/normalization.py +0 -0
  88. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/nn/pool.py +0 -0
  89. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/ops.py +0 -0
  90. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/quantization.py +0 -0
  91. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/specialized_fns.py +0 -0
  92. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/state_dict.py +0 -0
  93. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/tree_util.py +0 -0
  94. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/types.py +0 -0
  95. {haliax-1.4.dev364 → haliax-1.4.dev366}/src/haliax/util.py +0 -0
  96. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/core_test.py +0 -0
  97. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_axis.py +0 -0
  98. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_conv.py +0 -0
  99. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_debug.py +0 -0
  100. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_einsum.py +0 -0
  101. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_fp8.py +0 -0
  102. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_hof.py +0 -0
  103. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_int8.py +0 -0
  104. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_nn.py +0 -0
  105. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_ops.py +0 -0
  106. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_parsing.py +0 -0
  107. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_partitioning.py +0 -0
  108. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_pool.py +0 -0
  109. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_random.py +0 -0
  110. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_rearrange.py +0 -0
  111. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_scan.py +0 -0
  112. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_specialized_fns.py +0 -0
  113. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_state_dict.py +0 -0
  114. {haliax-1.4.dev364 → haliax-1.4.dev366}/tests/test_utils.py +0 -0
@@ -2,7 +2,7 @@
2
2
  # See https://pre-commit.com/hooks.html for more hooks
3
3
  exclude: ".git"
4
4
  default_stages:
5
- - commit
5
+ - pre-commit
6
6
  fail_fast: true
7
7
 
8
8
  repos:
@@ -16,24 +16,24 @@ repos:
16
16
  - id: check-merge-conflict
17
17
  - id: check-added-large-files
18
18
 
19
- - repo: https://github.com/psf/black
20
- rev: 22.3.0
21
- hooks:
22
- - id: black
23
-
24
- - repo: https://github.com/timothycrosley/isort
25
- rev: 5.11.5
26
- hooks:
27
- - id: isort
19
+ - repo: https://github.com/astral-sh/ruff-pre-commit
20
+ rev: v0.11.10
21
+ hooks:
22
+ - id: ruff
23
+ args: [ --fix, --exit-non-zero-on-fix ]
28
24
 
29
- - repo: https://github.com/PyCQA/flake8
30
- rev: 3.9.2
31
- hooks:
32
- - id: flake8
33
- additional_dependencies: [flake8-isort]
25
+ #- repo: local
26
+ # hooks:
27
+ # - id: ty-check
28
+ # name: ty-check
29
+ # language: python
30
+ # entry: ty check
31
+ # pass_filenames: false
32
+ # args: [--python=.venv/]
33
+ # additional_dependencies: [ty]
34
34
 
35
35
  - repo: https://github.com/pre-commit/mirrors-mypy
36
- rev: 'v1.5.1'
36
+ rev: 'v1.16.1'
37
37
  hooks:
38
38
  - id: mypy
39
39
  args: [--ignore-missing-imports, --check-untyped-defs]
@@ -0,0 +1,79 @@
1
+ # Haliax LLM Agent Guidelines
2
+
3
+ This document summarizes important conventions for contributing code or documentation to the Haliax
4
+ repository. Follow these notes when implementing new features or fixing bugs.
5
+
6
+ ## General Guidelines
7
+
8
+ * **Get better.** Whenever you discover something missing from these guidelines, or the requester
9
+ suggests a better way to do something, please update this document. The goal is to make it easier for
10
+ everyone to contribute and maintain the codebase. Generally speaking, you should add bullets or new sections.
11
+ Be sure to do this when directed to. For example, if directed that you should never relax tolerances in
12
+ floating point tests, add that to the list.
13
+ * **Playbooks.** Sometimes, there are repeatable tasks (e.g. porting models) for which we follow a standard set of steps.
14
+ Please reference `.playbooks/` to see what playbooks are available, or see the list below. If you want to add a playbook
15
+ write a markdown doc named e.g. `.playbooks/port-models.md` and add a pointer to it in the list below.
16
+
17
+ ## Playbook
18
+
19
+ - At the moment, there are no playbooks available. If you have a repeatable task that you think
20
+ should be documented, please create a new markdown file in `.playbooks/` and add it to the list above.
21
+
22
+ ## Code Style
23
+
24
+ * **Python version**: the project targets Python >=3.10.
25
+ * **Formatting and Linting**: We use `ruff` via `pre-commit`.
26
+ * **Typing**: the code base uses `mypy` for static type checking. `mypy` is run by pre‑commit and the
27
+ configuration is found in `pyproject.toml`.
28
+ * **Run `pre-commit run --all-files`** before committing. The CI workflows run the same checks.
29
+ * **Doc Strings**: All public functions, classes, and modules should have docstrings, unless
30
+ their purpose is painfully obvious. Use
31
+ [Google style](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings) for
32
+ consistency.
33
+ * **Commenting**: Use comments to explain why something is done a certain way, especially if it is not
34
+ immediately obvious. Avoid commenting on every line of code; focus on the intent and purpose of
35
+ complex logic. Demarcating logical groups of code with comments is encouraged, unless it is better
36
+ to refactor the code into smaller functions or classes.
37
+ * **Mkdocs**: We use [Mkdocs](https://www.mkdocs.org/) for documentation. The main documentation is in
38
+ the `docs` directory. Use Markdown for writing docs, and follow the existing structure. When linking to
39
+ symbols, prefer using mkdocs-style links (e.g. With a custom title: `[full.path.object2][]` or
40
+ `[Object 1][full.path.object1]`)
41
+ * **Documentation**: When adding new features, ensure that the documentation is updated accordingly.
42
+ This includes updating the Mkdocs files and any relevant docstrings. If you add a new module or
43
+ significant functionality, consider adding a dedicated section in the documentation.
44
+
45
+ ## Testing
46
+
47
+ * Tests are executed with `pytest`. The default workflow runs
48
+ `pytest tests -m "not entry and not slow and not ray"`.
49
+ * In general, never relax tolerances in floating point tests unless specifically discussed with the
50
+ team. Use `assert_allclose` with appropriate tolerances for numerical comparisons. We typically use
51
+ 1e-4 for more complex modules, and 1e-5 for simpler ones.
52
+ * Tests should be reasonably fast. Mark long-running tests with @pytest.mark.slow so they are excluded from the default suite.
53
+ * Always mark tests that depend on pytorch with `@skip_if_no_torch` to ensure they are skipped
54
+ when PyTorch is not available. This is particularly important for tests that require PyTorch-specific
55
+ functionality.
56
+
57
+
58
+ ## Design Preferences
59
+
60
+ * **Generic code**: many utilities are written with Python generics and dataclasses. Where possible,
61
+ write reusable functions or classes that operate over TypeVars instead of hard coding concrete types.
62
+ * **Configurations**: configuration files are dataclasses loaded via `draccus`. Keep configs
63
+ declarative and typed.
64
+ * **Reproducibility**: Levanter aims for deterministic training where possible. Avoid sources of
65
+ nondeterminism unless explicitly required.
66
+ * Prefer Stacked with fold or scan over writing custom loops, for better compile times and gradient checkpointing support
67
+
68
+ ## Library conventions
69
+ - Haliax revolves around `NamedArray` and explicit `Axis` objects. Prefer APIs that accept
70
+ axes or axis names rather than hard‑coding positional dimensions.
71
+ - Utilities should be written so they work with arbitrary axis names. Avoid relying on
72
+ fixed axis orders when possible.
73
+ - Use the provided modules in `haliax.nn` or Equinox when building neural network layers.
74
+ - Type annotations can use named shapes shorthand provided in `haliax.haxtyping`: `ht.f32[NamedArray, "batch"]`
75
+ for a float32 array with a "batch" axis, or `ht.Float[NamedArray, "batch"]` for any floating point dtype.
76
+
77
+ ## Documentation
78
+ - Public functions and modules require docstrings. If behavior is non‑obvious,
79
+ add examples in `docs/`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: haliax
3
- Version: 1.4.dev364
3
+ Version: 1.4.dev366
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/
@@ -29,6 +29,7 @@ Requires-Dist: mkdocs>=1.4.3; extra == 'dev'
29
29
  Requires-Dist: mkdocstrings-python>=1.1.2; extra == 'dev'
30
30
  Requires-Dist: mkdocstrings>=0.22.0; extra == 'dev'
31
31
  Requires-Dist: mypy>=0.910; extra == 'dev'
32
+ Requires-Dist: pre-commit; extra == 'dev'
32
33
  Requires-Dist: pygments; extra == 'dev'
33
34
  Requires-Dist: pymdown-extensions; extra == 'dev'
34
35
  Requires-Dist: pytest>=7.4.0; extra == 'dev'
@@ -55,7 +55,6 @@ We don't provide an explicit attention module, but we do provide an attention fu
55
55
 
56
56
  :::haliax.nn.attention.dot_product_attention
57
57
  :::haliax.nn.attention.dot_product_attention_weights
58
- :::haliax.nn.attention.self_attention
59
58
 
60
59
  ### Masks
61
60
  ::: haliax.nn.attention.causal_mask
@@ -0,0 +1,100 @@
1
+ from haliax import NamedArrayfrom haliax import NamedArray
2
+
3
+ # NamedArray Type Annotations
4
+
5
+ Haliax supports an extension to [`jaxtyping`](https://docs.kidger.site/jaxtyping/)
6
+ that allows you to annotate functions and methods that take or return
7
+ [`NamedArray`][haliax.core.NamedArray] objects. If you are familiar with
8
+ [`jaxtyping`](https://docs.kidger.site/jaxtyping/), the syntax is very similar.
9
+ In fact, for non-NamedArrays, it is exactly the same.
10
+
11
+ ```python
12
+ from haliax import NamedArray
13
+ import haliax.haxtyping as ht
14
+
15
+ def foo(x: ht.Float[NamedArray, "batch embed ..."]):
16
+ ...
17
+ ```
18
+
19
+ At runtime you can verify that a `NamedArray` conforms to a particular
20
+ annotation using `matches_axes`:
21
+
22
+ ```python
23
+ if not arr.matches_axes(Float[NamedArray, "batch embed ..."]):
24
+ raise ValueError("unexpected axes")
25
+ ```
26
+
27
+ ## DType-aware annotations
28
+
29
+ Sometimes it is useful to express both the axes **and** the dtype in the type
30
+ annotation. The :mod:`haliax.typing` module defines symbolic types for all of
31
+ JAX's common dtypes that can be indexed just like ``Named``. In documentation
32
+ examples we'll use ``import haliax.typing as ht``:
33
+
34
+ ```python
35
+ import haliax.haxtyping as ht
36
+
37
+ def foo(x: ht.f32[NamedArray, "batch"]):
38
+ ...
39
+
40
+ def bar(x: ht.i32[NamedArray, "batch"]):
41
+ ...
42
+ ```
43
+
44
+ For convenience the module also provides aggregate categories ``Float``,
45
+ ``Complex``, ``Int`` and ``UInt`` that match any floating point, complex,
46
+ signed integer or unsigned integer dtype respectively:
47
+
48
+ ```python
49
+ def baz(x: ht.Float[NamedArray, "batch"]):
50
+ ...
51
+ ```
52
+
53
+ At runtime ``matches_axes`` also checks the dtype when one is present:
54
+
55
+ ```python
56
+ from haliax import Axis, zeros
57
+ import haliax.haxtyping as ht
58
+
59
+ arr = zeros({"batch": 4})
60
+ assert arr.matches_axes(ht.f32["batch"]) # dtype and axes both match
61
+ ```
62
+
63
+ ## FAQ
64
+
65
+ ### Why not use `NamedArray` directly in type annotations?
66
+
67
+ Using `NamedArray` directly in type annotations doesn't work well with
68
+ type checkers like `mypy` or `pyright`. These tools expect types to be
69
+ subscripted with other types or forward references (which are strings).
70
+ Using `NamedArray` directly would lead to type errors.
71
+
72
+ ### Why not use `jaxtyping` directly?
73
+
74
+ While `jaxtyping` is a powerful library for type annotations in JAX, it does not
75
+ support `NamedArray` objects directly. The `haliax.haxtyping` module extends
76
+ `jaxtyping` to include `NamedArray` support, allowing you to annotate functions
77
+ and methods that take or return `NamedArray` objects with specific axes and dtypes.
78
+
79
+ ### Why do I have to specify the `NamedArray` type in the annotation?
80
+
81
+ I hate this, but it's the only way to get type checkers like `mypy` and `pyright` to understand that the type is
82
+ a `NamedArray`. Underneath the hood, during type checking, `jaxtyping.Float` (and `haxtyping.Float`) are
83
+ essentially type aliases of [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated)
84
+ with the `NamedArray` type. There's no other way I could find to get type checkers to understand that the type is a
85
+ `NamedArray` or to accept strings like `"batch embed ..."` as valid type annotations.
86
+
87
+ ### How do I use single axes in type annotations with flake or ruff.
88
+
89
+ Like `jaxtyping`, you need to prepend a space before the axis name to use single axes in type annotations with
90
+ flake or ruff. For example, to use a single axis named `batch`, you would write:
91
+
92
+ ```python
93
+ def foo(x: ht.Float[NamedArray, " batch"]):
94
+ ...
95
+ ```
96
+
97
+ Then suppress F722 in your linter to suppress that error.
98
+
99
+ See the [jaxtyping documentation](https://docs.kidger.site/jaxtyping/faq/#flake8-or-ruff-are-throwing-an-error) for more
100
+ details on the workaround.
@@ -38,7 +38,8 @@ dev=["pytest >= 7.4.0", "mypy >= 0.910", "mkdocs >= 1.4.3", "mkdocs-material >=
38
38
  "pymdown-extensions",
39
39
  "pygments",
40
40
  "pymdown-extensions",
41
- "chex>=0.1.86"
41
+ "chex>=0.1.86",
42
+ "pre-commit",
42
43
  ]
43
44
 
44
45
 
@@ -72,6 +73,11 @@ src_paths = ["src", "tests"]
72
73
  "Bug Tracker" = "https://github.com/stanford-crfm/haliax/issues/"
73
74
  "Documentation" = "https://haliax.readthedocs.io/en/latest/"
74
75
 
76
+ [tool.ruff.lint]
77
+ ignore = [ "E203", "E501", "W605", "F821", "E266", "F722", "E731", "E741" ]
78
+
79
+ [tool.setuptools.package-data]
80
+ "haliax" = ["*.pyi"]
75
81
 
76
82
  [tool.coverage.report]
77
83
  exclude_also = [
@@ -0,0 +1 @@
1
+ __version__ = "1.4.dev366"
@@ -1,4 +1,4 @@
1
- import typing
1
+ import typing as t
2
2
  from typing import Optional, Sequence
3
3
 
4
4
  import jax
@@ -42,11 +42,8 @@ from .axis import (
42
42
  to_jax_shape,
43
43
  )
44
44
  from .core import (
45
- Named,
46
45
  NamedArray,
47
- NamedArrayAxes,
48
- NamedArrayAxesSpec,
49
- NamedOrNumeric,
46
+ NamedArrayAxes, NamedArrayAxesSpec, NamedOrNumeric,
50
47
  are_shape_checks_enabled,
51
48
  broadcast_arrays,
52
49
  broadcast_axis,
@@ -66,6 +63,7 @@ from .core import (
66
63
  unflatten_axis,
67
64
  updated_slice,
68
65
  )
66
+ from .haxtyping import Named
69
67
  from .hof import fold, map, scan, vmap
70
68
  from .jax_utils import tree_checkpoint_name
71
69
  from .ops import clip, isclose, pad_left, trace, tril, triu, where
@@ -83,8 +81,8 @@ from .wrap import (
83
81
  )
84
82
 
85
83
 
86
- T = typing.TypeVar("T")
87
- A = typing.TypeVar("A", Scalar, NamedArray, jnp.ndarray)
84
+ T = t.TypeVar("T")
85
+ A = t.TypeVar("A", Scalar, NamedArray, jnp.ndarray)
88
86
 
89
87
 
90
88
  # creation routines
@@ -148,11 +146,6 @@ def arange(axis: AxisSpec, *, start=0, step=1, dtype: Optional[DTypeLike] = None
148
146
  ```
149
147
 
150
148
  """
151
- from haliax.jax_utils import to_jax_shape
152
- from haliax.util import ensure_tuple
153
-
154
- # if start is a tracer, we need to be a bit cleverer since arange doesn't support tracers
155
- # return NamedArray(jnp.arange(start, stop, step, dtype=dtype), (axis,))
156
149
  size = axis_size(axis)
157
150
 
158
151
  arr = jax.lax.iota(dtype=dtype or jnp.result_type(start), size=size) * step + start
@@ -274,7 +267,7 @@ def concatenate(axis: AxisSelector, arrays: Sequence[NamedArray]) -> NamedArray:
274
267
  if axis_index is None:
275
268
  raise ValueError(f"Axis {aname} not found in 0th array {arrays[0]}")
276
269
 
277
- axes: typing.Tuple[AxisSelector, ...] = arrays[0].axes
270
+ axes: tuple[AxisSelector, ...] = arrays[0].axes
278
271
  # we want to use the axis name for `axis`, because it's not uncommon for those to be different lengths in the arrays
279
272
  axes = axes[:axis_index] + (aname,) + axes[axis_index + 1 :]
280
273
  arrays = [a.rearrange(axes) for a in arrays]
@@ -931,9 +924,6 @@ __all__ = [
931
924
  "make_axes",
932
925
  "axis_name",
933
926
  "axis_size",
934
- "NamedArrayAxesSpec",
935
- "NamedArrayAxes",
936
- "Named",
937
927
  "NamedArray",
938
928
  "broadcast_to",
939
929
  "broadcast_axis",
@@ -1106,4 +1096,14 @@ __all__ = [
1106
1096
  "is_named_array",
1107
1097
  "tree_checkpoint_name",
1108
1098
  "ScanCheckpointPolicy",
1099
+ "quantization",
1100
+ "util",
1101
+ "einsum",
1102
+ "broadcast_arrays",
1103
+ "unflatten_axis",
1104
+ "ReductionFunction",
1105
+ "SimpleReductionFunction",
1106
+ "NamedArrayAxes",
1107
+ "NamedArrayAxesSpec",
1108
+ "Named",
1109
1109
  ]
@@ -4,7 +4,6 @@ import warnings
4
4
  from typing import Dict, Optional, Tuple
5
5
 
6
6
  import jax
7
- import jax.numpy as jnp
8
7
 
9
8
  import haliax
10
9
  from haliax.axis import (
@@ -20,7 +19,6 @@ from haliax.axis import (
20
19
  from haliax.core import NamedArray
21
20
  from haliax.jax_utils import _jittable_dg_einsum
22
21
  from haliax.types import DTypeLike, PrecisionLike
23
- from haliax.util import ensure_tuple
24
22
 
25
23
 
26
24
  # deprecated overload
@@ -1,4 +1,3 @@
1
- import warnings
2
1
  from functools import partial
3
2
 
4
3
  from jax import custom_jvp, custom_vjp, lax
@@ -15,8 +15,7 @@ from jaxtyping import PyTree
15
15
 
16
16
  import haliax.partitioning as partitioning
17
17
  from haliax._src.util import index_where
18
- from haliax.axis import Axis
19
- from haliax.core import NamedArray, flatten_axes, named
18
+ from haliax.core import NamedArray, named
20
19
  from haliax.jax_utils import is_jax_array_like, is_scalarish
21
20
  from haliax.tree_util import scan_aware_tree_map
22
21
 
@@ -209,14 +208,14 @@ def from_state_dict(tree: T, state_dict: StateDict, prefix: Optional[str] = None
209
208
  array = named(array, tree.axes)
210
209
  array = partitioning.auto_sharded(array)
211
210
 
212
- return array
211
+ return array # type: ignore
213
212
  elif is_jax_array_like(tree):
214
213
  if prefix is None:
215
214
  raise ValueError("Cannot extract a leaf value from a state dict without a prefix")
216
215
  # TODO: add "strict" flag so we can return None in cases where it's just missing
217
216
  return jnp.array(state_dict[prefix])
218
217
  elif tree is None:
219
- return None
218
+ return None # type: ignore
220
219
  else:
221
220
  if prefix is None:
222
221
  return tree
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import contextlib
2
4
  import functools as ft
3
5
  import typing
@@ -32,7 +34,6 @@ from .axis import (
32
34
  )
33
35
  from .types import GatherScatterModeStr, IntScalar, PrecisionLike, Scalar
34
36
 
35
-
36
37
  NamedOrNumeric = Union[Scalar, "NamedArray"]
37
38
  NamedIndex = Union[int, slice_t, "NamedArray", dslice, list[int], jnp.ndarray]
38
39
 
@@ -181,19 +182,6 @@ def _parse_namedarray_axes(
181
182
  raise TypeError(f"Invalid NamedArray typing spec: {item}")
182
183
 
183
184
 
184
- class Named:
185
- """Type annotation helper for :class:`NamedArray`.
186
-
187
- ``Named["batch embed"]`` expands to ``Annotated[NamedArray, axes]`` so that
188
- type checkers treat it as a ``NamedArray`` at static time while the axis
189
- metadata is available at runtime via :func:`typing.get_args`.
190
- """
191
-
192
- def __class_getitem__(cls, item: NamedArrayAxesSpec) -> typing.Any:
193
- axes = _parse_namedarray_axes(item)
194
- return typing.Annotated[NamedArray, axes]
195
-
196
-
197
185
  class NamedArrayMeta(type):
198
186
  def __getitem__(cls, item: NamedArrayAxesSpec) -> typing.Any:
199
187
  axes = _parse_namedarray_axes(item)
@@ -2112,7 +2100,6 @@ def _convert_index_expr_to_dict(idx) -> dict[AxisSelector, NamedIndex]:
2112
2100
  __all__ = [
2113
2101
  "NamedArrayAxesSpec",
2114
2102
  "NamedArrayAxes",
2115
- "Named",
2116
2103
  "NamedArray",
2117
2104
  "named",
2118
2105
  "slice",
@@ -0,0 +1,138 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+
6
+ @dataclass(frozen=True)
7
+ class DTypeCategory:
8
+ """Represents a dtype category such as ``float`` or ``int``."""
9
+
10
+ name: str
11
+ category: Any
12
+
13
+ def __repr__(self) -> str: # pragma: no cover - trivial
14
+ return self.name
15
+
16
+ if TYPE_CHECKING:
17
+ # ── STATIC ONLY: re‑export jaxtyping’s aliases so mypy/Pyright/PyCharm see them
18
+ from jaxtyping import (
19
+ Float32 as f32, Float64 as f64, Float16 as f16, BFloat16 as bf16,
20
+ Int8 as i8, Int16 as i16, Int32 as i32, Int64 as i64,
21
+ UInt8 as u8, UInt16 as u16, UInt32 as u32, UInt64 as u64,
22
+ Bool as bool_, Complex64 as complex64, Complex128 as complex128,
23
+ Float as Float, Int as Int, UInt as UInt,
24
+ )
25
+ # axes‑only helper
26
+ from typing import Annotated as Named
27
+
28
+ else:
29
+ # ── RUNTIME: custom wrappers for NamedArray, plus delegation to jaxtyping ──
30
+ import jaxtyping as jt
31
+ import jax.numpy as jnp
32
+ from typing import Annotated
33
+ from dataclasses import dataclass, replace
34
+
35
+ from .core import (
36
+ NamedArray,
37
+ NamedArrayAxes,
38
+ NamedArrayAxesSpec,
39
+ _parse_namedarray_axes,
40
+ )
41
+
42
+ def _with_dtype(axes: NamedArrayAxes, dtype):
43
+ """Attach dtype to axes metadata if not already set."""
44
+ return axes if axes.dtype is not None else replace(axes, dtype=dtype)
45
+
46
+ def _make_dtype_wrapper(dtype):
47
+ """Factory for f32, i32, etc."""
48
+ class _Wrapper:
49
+ def __class_getitem__(cls, item):
50
+ # two‑arg form: (BaseType, axes_spec)
51
+ if isinstance(item, tuple) and len(item) == 2:
52
+ base, axes_spec = item
53
+ else:
54
+ base, axes_spec = NamedArray, item
55
+
56
+ # Delegate non‑NamedArray to jaxtyping
57
+ if base is not NamedArray:
58
+ # e.g. use jt.Float32 for jnp.float32
59
+ jaxt = getattr(jt, f"Float{dtype.itemsize*8}") \
60
+ if hasattr(dtype, "itemsize") else jt.Float
61
+ return jaxt[base, axes_spec]
62
+
63
+ # Handle NamedArray path
64
+ axes = _parse_namedarray_axes(axes_spec)
65
+ return Annotated[NamedArray, _with_dtype(axes, dtype)]
66
+
67
+ return _Wrapper
68
+
69
+ # ── Build all dtype wrappers ─────────────────────────────────────────────
70
+ f32 = _make_dtype_wrapper(jnp.float32)
71
+ f64 = _make_dtype_wrapper(jnp.float64)
72
+ f16 = _make_dtype_wrapper(jnp.float16)
73
+ bf16 = _make_dtype_wrapper(jnp.bfloat16)
74
+
75
+ i8 = _make_dtype_wrapper(jnp.int8)
76
+ i16 = _make_dtype_wrapper(jnp.int16)
77
+ i32 = _make_dtype_wrapper(jnp.int32)
78
+ i64 = _make_dtype_wrapper(jnp.int64)
79
+
80
+ u8 = _make_dtype_wrapper(jnp.uint8)
81
+ u16 = _make_dtype_wrapper(jnp.uint16)
82
+ u32 = _make_dtype_wrapper(jnp.uint32)
83
+ u64 = _make_dtype_wrapper(jnp.uint64)
84
+
85
+ bool_ = _make_dtype_wrapper(jnp.bool_)
86
+ complex64 = _make_dtype_wrapper(jnp.complex64)
87
+ complex128 = _make_dtype_wrapper(jnp.complex128)
88
+
89
+
90
+
91
+
92
+ def _make_category_wrapper(name: str, category):
93
+ """Like _make_dtype_wrapper but matches any dtype in the JAX category."""
94
+
95
+ class _Wrapper:
96
+ def __class_getitem__(cls, item):
97
+ # same base/axes unpack logic
98
+ if isinstance(item, tuple) and len(item) == 2:
99
+ base, axes_spec = item
100
+ else:
101
+ base, axes_spec = NamedArray, item
102
+
103
+ # non‑NamedArray → delegate to jaxtyping’s category wrapper
104
+ if base is not NamedArray:
105
+ return getattr(jt, name)[base, axes_spec]
106
+
107
+ # NamedArray path
108
+ axes = _parse_namedarray_axes(axes_spec)
109
+ cat = DTypeCategory(name, category)
110
+ return Annotated[NamedArray, _with_dtype(axes, cat)]
111
+
112
+ return _Wrapper
113
+
114
+
115
+ # Build the category wrappers
116
+ Float = _make_category_wrapper("float", jnp.floating)
117
+ Complex = _make_category_wrapper("complex", jnp.complexfloating)
118
+ Int = _make_category_wrapper("int", jnp.signedinteger)
119
+ UInt = _make_category_wrapper("uInt", jnp.unsignedinteger)
120
+
121
+
122
+ # ── Named: axes‑only helper ───────────────────────────────────────────────
123
+ class _NamedHelper:
124
+ @classmethod
125
+ def __class_getitem__(self, axes_spec_: tuple[type[NamedArray], NamedArrayAxesSpec]):
126
+ _, axes_spec = axes_spec_
127
+ axes = _parse_namedarray_axes(axes_spec)
128
+ return Annotated[NamedArray, axes]
129
+
130
+ Named = _NamedHelper
131
+
132
+ __all__ = [
133
+ "Named",
134
+ "f32", "f64", "f16", "bf16",
135
+ "i8", "i16", "i32", "i64",
136
+ "u8", "u16", "u32", "u64",
137
+ "bool_", "complex64", "complex128",
138
+ ]
@@ -23,7 +23,7 @@ from .axis import Axis, AxisSelection, AxisSelector, axis_spec_to_shape_dict, ax
23
23
  from .core import NamedArray
24
24
  from .jax_utils import Static, broadcast_prefix, is_jax_array_like
25
25
  from .partitioning import physical_axis_name
26
- from .util import ensure_tuple, is_named_array
26
+ from .util import is_named_array
27
27
 
28
28
 
29
29
  def vmap(
@@ -11,7 +11,6 @@ import haliax.random as hrandom
11
11
  from haliax.axis import Axis, AxisSelection, AxisSelector, AxisSpec, axis_name, axis_spec_to_shape_dict
12
12
  from haliax.core import NamedArray
13
13
  from haliax.types import PrecisionLike
14
- from haliax.util import ensure_tuple
15
14
 
16
15
 
17
16
  # With attention, we usually distinguish between the mask and the bias, though the former is just a special case of the
@@ -1,5 +1,4 @@
1
1
  import dataclasses
2
- import math
3
2
  import warnings
4
3
  from typing import Optional
5
4
 
@@ -12,7 +11,6 @@ from ..axis import Axis, AxisSpec, concat_axes
12
11
  from ..core import NamedArray
13
12
  from ..jax_utils import named_call
14
13
  from ..tree_util import resize_axis
15
- from ..util import ensure_tuple
16
14
 
17
15
 
18
16
  class Embedding(eqx.Module):
@@ -2,7 +2,7 @@ import dataclasses
2
2
  import functools
3
3
  import re
4
4
  import warnings
5
- from typing import Any, Dict, Generic, Literal, Optional, Protocol, Sequence, Type, TypeVar, Union, cast
5
+ from typing import Any, Dict, Generic, Optional, Protocol, Sequence, Type, TypeVar, cast
6
6
 
7
7
  import equinox as eqx
8
8
  import jax
@@ -169,7 +169,7 @@ class BlockSeq(ModuleWithStateDictSerialization, Generic[M]):
169
169
 
170
170
  @staticmethod
171
171
  def _slice_out(Block, i, x):
172
- if haliax.is_named_array(x):
172
+ if isinstance(x, haliax.core.NamedArray):
173
173
  if haliax.selects_axis(x.axes, Block):
174
174
  return x[Block, i]
175
175
  else:
@@ -411,7 +411,7 @@ class Stacked(ModuleWithStateDictSerialization, Generic[M]):
411
411
  else:
412
412
  return tuple(x for _ in range(self.Block.size))
413
413
 
414
- leaves, structure = jax.tree_util.tree_flatten(self.stacked, is_leaf=haliax.is_named_array)
414
+ leaves, structure = jax.tree_util.tree_flatten(self.stacked, is_leaf=haliax.util.is_named_array)
415
415
  unstacked_leaves = tuple(map(unbatch_leaf, leaves))
416
416
  # now we need to transpose the leaves
417
417
  unstacked_leaves = tuple(zip(*unstacked_leaves))
@@ -20,7 +20,7 @@ from .axis import Axis, AxisSelection, AxisSelector, axis_spec_to_shape_dict
20
20
  from .core import NamedArray
21
21
  from .jax_utils import Static, is_in_jit, is_jax_array_like, is_on_mac_metal
22
22
  from .tree_util import hashable_combine, hashable_partition
23
- from .util import StringHolderEnum, ensure_tuple
23
+ from .util import StringHolderEnum
24
24
 
25
25
 
26
26
  PhysicalAxisSpec = Union[(str), Sequence[str]]
@@ -1,7 +1,4 @@
1
1
  """Wrappers around jax.random functions."""
2
- import functools
3
- import inspect
4
- import warnings
5
2
  from typing import Optional
6
3
 
7
4
  import jax.random as jrandom