haliax 1.4.dev400__tar.gz → 1.4.dev403__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 (120) hide show
  1. {haliax-1.4.dev400 → haliax-1.4.dev403}/PKG-INFO +1 -1
  2. haliax-1.4.dev403/src/haliax/__about__.py +1 -0
  3. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/__init__.py +2 -0
  4. haliax-1.4.dev403/src/haliax/field.py +38 -0
  5. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/partitioning.py +90 -30
  6. haliax-1.4.dev403/tests/test_field.py +19 -0
  7. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_partitioning.py +48 -1
  8. haliax-1.4.dev400/src/haliax/__about__.py +0 -1
  9. {haliax-1.4.dev400 → haliax-1.4.dev403}/.coveragerc +0 -0
  10. {haliax-1.4.dev400 → haliax-1.4.dev403}/.flake8 +0 -0
  11. {haliax-1.4.dev400 → haliax-1.4.dev403}/.github/workflows/publish_dev.yaml +0 -0
  12. {haliax-1.4.dev400 → haliax-1.4.dev403}/.github/workflows/run_pre_commit.yaml +0 -0
  13. {haliax-1.4.dev400 → haliax-1.4.dev403}/.github/workflows/run_quick_levanter_tests.yaml +0 -0
  14. {haliax-1.4.dev400 → haliax-1.4.dev403}/.github/workflows/run_tests.yaml +0 -0
  15. {haliax-1.4.dev400 → haliax-1.4.dev403}/.gitignore +0 -0
  16. {haliax-1.4.dev400 → haliax-1.4.dev403}/.playbooks/add-types.md +0 -0
  17. {haliax-1.4.dev400 → haliax-1.4.dev403}/.playbooks/wrap-non-named.md +0 -0
  18. {haliax-1.4.dev400 → haliax-1.4.dev403}/.pre-commit-config.yaml +0 -0
  19. {haliax-1.4.dev400 → haliax-1.4.dev403}/.readthedocs.yaml +0 -0
  20. {haliax-1.4.dev400 → haliax-1.4.dev403}/AGENTS.md +0 -0
  21. {haliax-1.4.dev400 → haliax-1.4.dev403}/CONTRIBUTING.md +0 -0
  22. {haliax-1.4.dev400 → haliax-1.4.dev403}/LICENSE +0 -0
  23. {haliax-1.4.dev400 → haliax-1.4.dev403}/README.md +0 -0
  24. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/api.md +0 -0
  25. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/broadcasting.md +0 -0
  26. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/cheatsheet.md +0 -0
  27. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/css/material.css +0 -0
  28. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/css/mkdocstrings.css +0 -0
  29. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/faq.md +0 -0
  30. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/figures/data_parallel_mesh.png +0 -0
  31. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/figures/data_parallel_mesh_replicated.png +0 -0
  32. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/figures/device_mesh_1d.png +0 -0
  33. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/figures/device_mesh_1d_zero.png +0 -0
  34. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/figures/device_mesh_2d.png +0 -0
  35. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/figures/device_mesh_2d_batch_partitioned.png +0 -0
  36. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/figures/device_mesh_2d_data_replicated.png +0 -0
  37. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/figures/device_mesh_2d_data_replicated_mlp_partitioned.png +0 -0
  38. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/figures/device_mesh_2d_intermediate_fully_partitioned.png +0 -0
  39. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/figures/device_mesh_2d_zero.png +0 -0
  40. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/fp8.md +0 -0
  41. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/index.md +0 -0
  42. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/indexing.md +0 -0
  43. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/matmul.md +0 -0
  44. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/nn.md +0 -0
  45. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/partitioning.md +0 -0
  46. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/primer.md +0 -0
  47. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/rearrange.ipynb +0 -0
  48. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/rearrange.md +0 -0
  49. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/requirements.txt +0 -0
  50. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/scan.md +0 -0
  51. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/state-dict.md +0 -0
  52. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/tutorial.md +0 -0
  53. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/typing.md +0 -0
  54. {haliax-1.4.dev400 → haliax-1.4.dev403}/docs/vmap.md +0 -0
  55. {haliax-1.4.dev400 → haliax-1.4.dev403}/mkdocs.yml +0 -0
  56. {haliax-1.4.dev400 → haliax-1.4.dev403}/pyproject.toml +0 -0
  57. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/_src/__init__.py +0 -0
  58. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/_src/compile_utils.py +0 -0
  59. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/_src/dot.py +0 -0
  60. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/_src/einsum.py +0 -0
  61. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/_src/fp8.py +0 -0
  62. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/_src/parsing.py +0 -0
  63. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/_src/rearrange.py +0 -0
  64. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/_src/scan.py +0 -0
  65. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/_src/state_dict.py +0 -0
  66. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/_src/util.py +0 -0
  67. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/axis.py +0 -0
  68. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/core.py +0 -0
  69. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/debug.py +0 -0
  70. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/haxtyping.py +0 -0
  71. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/hof.py +0 -0
  72. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/jax_utils.py +0 -0
  73. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/nn/__init__.py +0 -0
  74. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/nn/activations.py +0 -0
  75. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/nn/attention.py +0 -0
  76. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/nn/conv.py +0 -0
  77. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/nn/dropout.py +0 -0
  78. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/nn/embedding.py +0 -0
  79. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/nn/linear.py +0 -0
  80. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/nn/loss.py +0 -0
  81. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/nn/mlp.py +0 -0
  82. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/nn/normalization.py +0 -0
  83. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/nn/pool.py +0 -0
  84. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/nn/scan.py +0 -0
  85. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/ops.py +0 -0
  86. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/quantization.py +0 -0
  87. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/random.py +0 -0
  88. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/specialized_fns.py +0 -0
  89. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/state_dict.py +0 -0
  90. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/tree_util.py +0 -0
  91. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/types.py +0 -0
  92. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/util.py +0 -0
  93. {haliax-1.4.dev400 → haliax-1.4.dev403}/src/haliax/wrap.py +0 -0
  94. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/core_test.py +0 -0
  95. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_attention.py +0 -0
  96. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_axis.py +0 -0
  97. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_conv.py +0 -0
  98. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_debug.py +0 -0
  99. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_dot.py +0 -0
  100. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_dtype_typing.py +0 -0
  101. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_einsum.py +0 -0
  102. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_fp8.py +0 -0
  103. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_hof.py +0 -0
  104. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_int8.py +0 -0
  105. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_moe_linear.py +0 -0
  106. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_namedarray_typing.py +0 -0
  107. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_nn.py +0 -0
  108. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_ops.py +0 -0
  109. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_parsing.py +0 -0
  110. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_pool.py +0 -0
  111. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_random.py +0 -0
  112. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_rearrange.py +0 -0
  113. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_scan.py +0 -0
  114. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_scatter_gather.py +0 -0
  115. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_specialized_fns.py +0 -0
  116. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_state_dict.py +0 -0
  117. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_tree_util.py +0 -0
  118. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_utils.py +0 -0
  119. {haliax-1.4.dev400 → haliax-1.4.dev403}/tests/test_visualize_sharding.py +0 -0
  120. {haliax-1.4.dev400 → haliax-1.4.dev403}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: haliax
3
- Version: 1.4.dev400
3
+ Version: 1.4.dev403
4
4
  Summary: Named Tensors for Legible Deep Learning in JAX
5
5
  Project-URL: Homepage, https://github.com/stanford-crfm/haliax
6
6
  Project-URL: Bug Tracker, https://github.com/stanford-crfm/haliax/issues/
@@ -0,0 +1 @@
1
+ __version__ = "1.4.dev403"
@@ -17,6 +17,7 @@ import haliax.random as random
17
17
  import haliax.state_dict as state_dict
18
18
  import haliax.tree_util as tree_util
19
19
  import haliax.util as util
20
+ from .field import field
20
21
 
21
22
  from ._src.dot import dot
22
23
  from ._src.einsum import einsum
@@ -932,6 +933,7 @@ __all__ = [
932
933
  "tree_util",
933
934
  "nn",
934
935
  "state_dict",
936
+ "field",
935
937
  "Axis",
936
938
  "AxisSpec",
937
939
  "AxisSelection",
@@ -0,0 +1,38 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Callable
4
+
5
+ import equinox as eqx
6
+
7
+
8
+ def field(
9
+ *,
10
+ converter: Callable[[Any], Any] | None = None,
11
+ static: bool = False,
12
+ axis_names: tuple[str, ...] | None = None,
13
+ **kwargs,
14
+ ):
15
+ """Wrapper around :func:`equinox.field` with optional ``axis_names`` metadata.
16
+
17
+ Args:
18
+ converter: Optional function applied to the value during dataclass initialisation.
19
+ static: Whether the field is static in the PyTree.
20
+ axis_names: Optional axis names associated with array fields. Cannot be
21
+ specified together with ``static=True``.
22
+ **kwargs: Additional keyword arguments forwarded to :func:`dataclasses.field`.
23
+
24
+ Returns:
25
+ A dataclasses field configured like :func:`equinox.field` with additional
26
+ ``axis_names`` metadata.
27
+ """
28
+ if static and axis_names is not None:
29
+ raise ValueError("axis_names cannot be specified together with static=True")
30
+
31
+ metadata = dict(kwargs.pop("metadata", {}))
32
+ metadata["axis_names"] = axis_names
33
+
34
+ field_kwargs = {}
35
+ if converter is not None:
36
+ field_kwargs["converter"] = converter
37
+
38
+ return eqx.field(static=static, metadata=metadata, **field_kwargs, **kwargs)
@@ -1,4 +1,5 @@
1
1
  import contextlib
2
+ import dataclasses
2
3
  import functools
3
4
  import threading
4
5
  import typing
@@ -177,24 +178,21 @@ def shard_with_axis_mapping(x: T, mapping: ResourceMapping, mesh: Optional[Mesh]
177
178
  return shard(x, mapping, mesh)
178
179
 
179
180
 
180
- def infer_resource_partitions(
181
+ def pspec_for(
181
182
  tree: PyTree,
182
183
  resource_mapping: Optional[ResourceMapping] = None,
183
184
  preserve_existing_shardings: bool = True,
184
185
  use_auto_sharding: bool = True,
185
- mesh: Optional[Mesh] = None,
186
186
  ) -> PyTree:
187
- """
188
- Infer the sharding for a module, to be used with named_jit.
189
- The basic idea is to tree all NamedArrays as leaves for the purposes of this function,
190
- and to create NamedShardings from those names plus the resource_mapping.
191
- If preserve_existing_shardings is True, then NamedArrays that are already sharded are left alone.
187
+ """Infer the :class:`PartitionSpec` for a module.
192
188
 
193
- If resource_mapping is not provided, this function attempts to use the global resource mapping.
189
+ This behaves like :func:`infer_resource_partitions` but returns ``PartitionSpec``
190
+ objects instead of :class:`~jax.sharding.NamedSharding`. It is primarily a helper
191
+ for :func:`infer_resource_partitions` but may be useful when only the partition
192
+ specification is required.
194
193
 
195
- If use_auto_sharding is True, then we use the new experimental AUTO-sharding feature, which is not yet
196
- fully supported by JAX. If it is False, then we will guess fully replicated for any unnamed arrays that
197
- don't have a sharding.
194
+ If ``preserve_existing_shardings`` is ``True``, then arrays that already have a
195
+ sharding are left untouched and ``None`` is returned for those leaves.
198
196
  """
199
197
  if resource_mapping is None:
200
198
  resource_mapping = current_thread_local_mapping()
@@ -202,35 +200,57 @@ def infer_resource_partitions(
202
200
  if resource_mapping is None:
203
201
  raise ValueError("No resource mapping found")
204
202
 
205
- mesh = mesh or _get_mesh()
206
- assert not isinstance(mesh, dict)
207
-
208
203
  def partition_spec(node: typing.Any):
209
204
  if isinstance(node, NamedArray):
210
205
  # If our NamedArray doesn't have an array (or a shapedtypestruct), we can't shard it
211
- # so better to not try
212
206
  if not is_jax_array_like(node.array):
213
207
  return None
214
208
 
215
- if preserve_existing_shardings:
216
- current_sharding = getattr(node.array, "sharding", None)
217
- else:
218
- current_sharding = None
219
-
209
+ current_sharding = getattr(node.array, "sharding", None) if preserve_existing_shardings else None
220
210
  if current_sharding is not None:
221
- return current_sharding
211
+ return None
222
212
  else:
223
- sharding = NamedSharding(mesh, pspec_for_axis(node.axes, resource_mapping))
224
- return sharding
213
+ return pspec_for_axis(node.axes, resource_mapping)
214
+ elif isinstance(node, eqx.Module):
215
+ # handle eqx.Module explicitly so that we can look at axis_names metadata
216
+ updates: dict[str, typing.Any] = {}
217
+ for field in dataclasses.fields(node):
218
+ if field.metadata.get("static", False):
219
+ continue
220
+
221
+ value = getattr(node, field.name)
222
+ axis_names = field.metadata.get("axis_names") if field.metadata is not None else None
223
+ if axis_names is not None and is_jax_array_like(value):
224
+ current_sharding = (
225
+ getattr(value, "sharding", None) if preserve_existing_shardings else None
226
+ )
227
+ if current_sharding is not None:
228
+ updates[field.name] = None
229
+ else:
230
+ updates[field.name] = pspec_for_axis(axis_names, resource_mapping)
231
+ else:
232
+ updates[field.name] = htu.tree_map(
233
+ partition_spec, value, is_leaf=lambda x: isinstance(x, eqx.Module)
234
+ )
235
+
236
+ new_node = object.__new__(type(node))
237
+ for field in dataclasses.fields(node):
238
+ object.__setattr__(
239
+ new_node,
240
+ field.name,
241
+ updates.get(field.name, getattr(node, field.name)),
242
+ )
243
+
244
+ return new_node
225
245
  elif is_jax_array_like(node):
226
246
  sharding = getattr(node, "sharding", None)
227
247
  # TODO: these are usually replicated. Is there a better way to tell?
228
248
  if node.shape == ():
229
- return NamedSharding(mesh, PartitionSpec())
249
+ return PartitionSpec()
230
250
  elif isinstance(sharding, SingleDeviceSharding):
231
- return NamedSharding(mesh, PartitionSpec(None))
232
- elif sharding is not None:
233
- return sharding
251
+ return PartitionSpec(None)
252
+ elif sharding is not None and preserve_existing_shardings:
253
+ return None
234
254
  # elif use_auto_sharding:
235
255
  # TODO: auto doesn't seem to really work reliably yet
236
256
  # compat between 0.4.10 and 0.4.11
@@ -238,13 +258,52 @@ def infer_resource_partitions(
238
258
  # return AUTO(mesh)
239
259
  # else:
240
260
  # return AUTO
241
- return NamedSharding(mesh, PartitionSpec(None))
261
+ return PartitionSpec(None)
242
262
  elif isinstance(node, (bool, float, complex, int)):
243
- return NamedSharding(mesh, PartitionSpec())
263
+ return PartitionSpec()
244
264
  else:
245
265
  return None
246
266
 
247
- return htu.tree_map(partition_spec, tree)
267
+ return htu.tree_map(partition_spec, tree, is_leaf=lambda x: isinstance(x, eqx.Module))
268
+
269
+
270
+ def infer_resource_partitions(
271
+ tree: PyTree,
272
+ resource_mapping: Optional[ResourceMapping] = None,
273
+ preserve_existing_shardings: bool = True,
274
+ use_auto_sharding: bool = True,
275
+ mesh: Optional[Mesh] = None,
276
+ ) -> PyTree:
277
+ """
278
+ Infer the sharding for a module, to be used with ``named_jit``.
279
+
280
+ This first calls :func:`pspec_for` to compute ``PartitionSpec`` objects and then
281
+ wraps them in :class:`~jax.sharding.NamedSharding` using the provided mesh. If
282
+ ``preserve_existing_shardings`` is ``True``, then arrays that are already sharded
283
+ retain their current sharding.
284
+ """
285
+ pspecs = pspec_for(
286
+ tree,
287
+ resource_mapping=resource_mapping,
288
+ preserve_existing_shardings=preserve_existing_shardings,
289
+ use_auto_sharding=use_auto_sharding,
290
+ )
291
+
292
+ mesh = mesh or _get_mesh()
293
+ assert not isinstance(mesh, dict)
294
+
295
+ def to_sharding(node: typing.Any, spec: typing.Any):
296
+ if spec is None:
297
+ if isinstance(node, NamedArray):
298
+ return getattr(node.array, "sharding", None)
299
+ elif is_jax_array_like(node):
300
+ return getattr(node, "sharding", None)
301
+ else:
302
+ return None
303
+ else:
304
+ return NamedSharding(mesh, spec)
305
+
306
+ return htu.tree_map(to_sharding, tree, pspecs)
248
307
 
249
308
 
250
309
  class WrappedCallable(typing.Protocol[Args, R]):
@@ -659,6 +718,7 @@ __all__ = [
659
718
  "auto_sharded",
660
719
  "shard",
661
720
  "shard_with_axis_mapping",
721
+ "pspec_for",
662
722
  "infer_resource_partitions",
663
723
  "named_jit",
664
724
  "fsdp",
@@ -0,0 +1,19 @@
1
+ import jax.numpy as jnp
2
+ import equinox as eqx
3
+ import pytest
4
+
5
+ import haliax as hax
6
+
7
+
8
+ class M(eqx.Module):
9
+ a: jnp.ndarray = hax.field(axis_names=("batch",))
10
+
11
+
12
+ def test_axis_names_metadata():
13
+ field = M.__dataclass_fields__["a"]
14
+ assert field.metadata["axis_names"] == ("batch",)
15
+
16
+
17
+ def test_axis_names_static_exclusive():
18
+ with pytest.raises(ValueError):
19
+ hax.field(static=True, axis_names=("x",))
@@ -7,7 +7,13 @@ from jaxtyping import Array
7
7
 
8
8
  import haliax as hax
9
9
  from haliax import Axis, NamedArray
10
- from haliax.partitioning import ResourceAxis, axis_mapping, infer_resource_partitions, named_jit
10
+ from haliax.partitioning import (
11
+ ResourceAxis,
12
+ axis_mapping,
13
+ infer_resource_partitions,
14
+ named_jit,
15
+ pspec_for,
16
+ )
11
17
  from test_utils import skip_if_not_enough_devices
12
18
 
13
19
 
@@ -40,6 +46,47 @@ def test_infer_named_axes():
40
46
  assert axes.unnamed1.is_fully_replicated
41
47
 
42
48
 
49
+ def test_pspec_for_named_axes():
50
+ mesh = Mesh(np.array(jax.devices()).reshape(-1, 1), (ResourceAxis.DATA, ResourceAxis.MODEL))
51
+ with axis_mapping(resource_map), mesh:
52
+ mod = MyModule(named=hax.ones((Dim1, Dim2, Dim3)), unnamed1=jnp.ones(Dim2.size), static_field=1)
53
+
54
+ specs: MyModule = pspec_for(mod, preserve_existing_shardings=False)
55
+
56
+ spec = PartitionSpec(None, ResourceAxis.DATA, ResourceAxis.MODEL)
57
+
58
+ assert specs.named == spec
59
+ assert specs.unnamed1 == PartitionSpec(None)
60
+
61
+
62
+ class ArrayModule(eqx.Module):
63
+ arr: Array = hax.field(axis_names=("dim2", "dim3"))
64
+
65
+
66
+ def test_pspec_for_plain_array_axis_names():
67
+ mesh = Mesh(np.array(jax.devices()).reshape(-1, 1), (ResourceAxis.DATA, ResourceAxis.MODEL))
68
+ with axis_mapping(resource_map), mesh:
69
+ mod = ArrayModule(jnp.ones((Dim2.size, Dim3.size)))
70
+
71
+ specs: ArrayModule = pspec_for(mod, preserve_existing_shardings=False)
72
+
73
+ assert specs.arr == PartitionSpec(ResourceAxis.DATA, ResourceAxis.MODEL)
74
+
75
+
76
+ class NestedArrayModule(eqx.Module):
77
+ inner: ArrayModule
78
+
79
+
80
+ def test_pspec_for_plain_array_axis_names_nested_module():
81
+ mesh = Mesh(np.array(jax.devices()).reshape(-1, 1), (ResourceAxis.DATA, ResourceAxis.MODEL))
82
+ with axis_mapping(resource_map), mesh:
83
+ mod = NestedArrayModule(ArrayModule(jnp.ones((Dim2.size, Dim3.size))))
84
+
85
+ specs: NestedArrayModule = pspec_for(mod, preserve_existing_shardings=False)
86
+
87
+ assert specs.inner.arr == PartitionSpec(ResourceAxis.DATA, ResourceAxis.MODEL)
88
+
89
+
43
90
  class MyModuleInit(eqx.Module):
44
91
  named: NamedArray
45
92
  unnamed1: Array
@@ -1 +0,0 @@
1
- __version__ = "1.4.dev400"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes