haliax 1.4.dev346__tar.gz → 1.4.dev348__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 (107) hide show
  1. {haliax-1.4.dev346 → haliax-1.4.dev348}/PKG-INFO +1 -1
  2. haliax-1.4.dev348/src/haliax/__about__.py +1 -0
  3. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/nn/__init__.py +2 -1
  4. haliax-1.4.dev348/src/haliax/nn/linear.py +261 -0
  5. haliax-1.4.dev346/src/haliax/__about__.py +0 -1
  6. haliax-1.4.dev346/src/haliax/nn/linear.py +0 -126
  7. {haliax-1.4.dev346 → haliax-1.4.dev348}/.coveragerc +0 -0
  8. {haliax-1.4.dev346 → haliax-1.4.dev348}/.flake8 +0 -0
  9. {haliax-1.4.dev346 → haliax-1.4.dev348}/.github/workflows/publish_dev.yaml +0 -0
  10. {haliax-1.4.dev346 → haliax-1.4.dev348}/.github/workflows/run_pre_commit.yaml +0 -0
  11. {haliax-1.4.dev346 → haliax-1.4.dev348}/.github/workflows/run_quick_levanter_tests.yaml +0 -0
  12. {haliax-1.4.dev346 → haliax-1.4.dev348}/.github/workflows/run_tests.yaml +0 -0
  13. {haliax-1.4.dev346 → haliax-1.4.dev348}/.gitignore +0 -0
  14. {haliax-1.4.dev346 → haliax-1.4.dev348}/.pre-commit-config.yaml +0 -0
  15. {haliax-1.4.dev346 → haliax-1.4.dev348}/.readthedocs.yaml +0 -0
  16. {haliax-1.4.dev346 → haliax-1.4.dev348}/CONTRIBUTING.md +0 -0
  17. {haliax-1.4.dev346 → haliax-1.4.dev348}/LICENSE +0 -0
  18. {haliax-1.4.dev346 → haliax-1.4.dev348}/README.md +0 -0
  19. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/api.md +0 -0
  20. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/broadcasting.md +0 -0
  21. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/cheatsheet.md +0 -0
  22. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/css/material.css +0 -0
  23. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/css/mkdocstrings.css +0 -0
  24. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/faq.md +0 -0
  25. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/figures/data_parallel_mesh.png +0 -0
  26. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/figures/data_parallel_mesh_replicated.png +0 -0
  27. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/figures/device_mesh_1d.png +0 -0
  28. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/figures/device_mesh_1d_zero.png +0 -0
  29. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/figures/device_mesh_2d.png +0 -0
  30. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/figures/device_mesh_2d_batch_partitioned.png +0 -0
  31. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/figures/device_mesh_2d_data_replicated.png +0 -0
  32. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/figures/device_mesh_2d_data_replicated_mlp_partitioned.png +0 -0
  33. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/figures/device_mesh_2d_intermediate_fully_partitioned.png +0 -0
  34. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/figures/device_mesh_2d_zero.png +0 -0
  35. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/fp8.md +0 -0
  36. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/index.md +0 -0
  37. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/indexing.md +0 -0
  38. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/matmul.md +0 -0
  39. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/nn.md +0 -0
  40. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/partitioning.md +0 -0
  41. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/rearrange.ipynb +0 -0
  42. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/rearrange.md +0 -0
  43. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/requirements.txt +0 -0
  44. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/scan.md +0 -0
  45. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/state-dict.md +0 -0
  46. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/tutorial.md +0 -0
  47. {haliax-1.4.dev346 → haliax-1.4.dev348}/docs/vmap.md +0 -0
  48. {haliax-1.4.dev346 → haliax-1.4.dev348}/mkdocs.yml +0 -0
  49. {haliax-1.4.dev346 → haliax-1.4.dev348}/pyproject.toml +0 -0
  50. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/__init__.py +0 -0
  51. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/_src/__init__.py +0 -0
  52. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/_src/compile_utils.py +0 -0
  53. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/_src/dot.py +0 -0
  54. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/_src/einsum.py +0 -0
  55. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/_src/fp8.py +0 -0
  56. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/_src/parsing.py +0 -0
  57. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/_src/rearrange.py +0 -0
  58. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/_src/scan.py +0 -0
  59. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/_src/state_dict.py +0 -0
  60. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/_src/util.py +0 -0
  61. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/axis.py +0 -0
  62. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/core.py +0 -0
  63. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/debug.py +0 -0
  64. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/hof.py +0 -0
  65. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/jax_utils.py +0 -0
  66. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/nn/activations.py +0 -0
  67. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/nn/attention.py +0 -0
  68. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/nn/conv.py +0 -0
  69. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/nn/dropout.py +0 -0
  70. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/nn/embedding.py +0 -0
  71. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/nn/loss.py +0 -0
  72. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/nn/mlp.py +0 -0
  73. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/nn/normalization.py +0 -0
  74. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/nn/pool.py +0 -0
  75. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/nn/scan.py +0 -0
  76. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/ops.py +0 -0
  77. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/partitioning.py +0 -0
  78. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/quantization.py +0 -0
  79. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/random.py +0 -0
  80. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/specialized_fns.py +0 -0
  81. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/state_dict.py +0 -0
  82. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/tree_util.py +0 -0
  83. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/types.py +0 -0
  84. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/util.py +0 -0
  85. {haliax-1.4.dev346 → haliax-1.4.dev348}/src/haliax/wrap.py +0 -0
  86. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/core_test.py +0 -0
  87. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/test_attention.py +0 -0
  88. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/test_axis.py +0 -0
  89. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/test_conv.py +0 -0
  90. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/test_debug.py +0 -0
  91. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/test_dot.py +0 -0
  92. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/test_einsum.py +0 -0
  93. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/test_fp8.py +0 -0
  94. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/test_hof.py +0 -0
  95. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/test_int8.py +0 -0
  96. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/test_nn.py +0 -0
  97. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/test_ops.py +0 -0
  98. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/test_parsing.py +0 -0
  99. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/test_partitioning.py +0 -0
  100. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/test_pool.py +0 -0
  101. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/test_random.py +0 -0
  102. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/test_rearrange.py +0 -0
  103. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/test_scan.py +0 -0
  104. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/test_specialized_fns.py +0 -0
  105. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/test_state_dict.py +0 -0
  106. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/test_tree_util.py +0 -0
  107. {haliax-1.4.dev346 → haliax-1.4.dev348}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: haliax
3
- Version: 1.4.dev346
3
+ Version: 1.4.dev348
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.dev348"
@@ -33,7 +33,7 @@ from .activations import (
33
33
  from .conv import Conv, ConvTranspose
34
34
  from .dropout import Dropout, dropout
35
35
  from .embedding import Embedding
36
- from .linear import Linear
36
+ from .linear import Linear, MoELinear
37
37
  from .loss import binary_cross_entropy_loss, cross_entropy_loss, cross_entropy_loss_and_log_normalizers, reduce_loss
38
38
  from .mlp import MLP
39
39
  from .normalization import LayerNorm, RmsNorm, log_softmax, logsumexp, softmax, standardize
@@ -83,6 +83,7 @@ __all__ = [
83
83
  "dropout",
84
84
  "LayerNorm",
85
85
  "Linear",
86
+ "MoELinear",
86
87
  "Embedding",
87
88
  "RmsNorm",
88
89
  "Stacked",
@@ -0,0 +1,261 @@
1
+ import dataclasses
2
+ import math
3
+ from functools import partial
4
+ from typing import Optional
5
+
6
+ import equinox as eqx
7
+ import jax
8
+ import jax.numpy as jnp
9
+ from jax.experimental.pallas.ops.tpu.megablox import gmm
10
+ from jax.experimental.shard_map import shard_map
11
+ from jax.random import PRNGKey
12
+
13
+ import haliax as hax
14
+
15
+ from .._src.state_dict import Mod, ModuleWithStateDictSerialization
16
+ from ..axis import Axis, AxisSpec
17
+ from ..core import NamedArray
18
+ from ..jax_utils import named_call
19
+ from ..partitioning import ResourceAxis
20
+ from ..quantization import DotGeneralOp
21
+
22
+
23
+ class Linear(ModuleWithStateDictSerialization):
24
+ """A named Linear layer. This module allows you to specify multiple named axes for both input
25
+ and output, which is occasionally useful."""
26
+
27
+ weight: NamedArray
28
+ bias: Optional[NamedArray]
29
+
30
+ In: AxisSpec = eqx.static_field()
31
+ Out: AxisSpec = eqx.static_field()
32
+ dot_general: DotGeneralOp = eqx.field(default_factory=DotGeneralOp.default)
33
+
34
+ @staticmethod
35
+ def init(
36
+ In: AxisSpec,
37
+ Out: AxisSpec,
38
+ *,
39
+ key: PRNGKey,
40
+ use_bias: bool = True,
41
+ out_first: bool = True,
42
+ dot_general: Optional[DotGeneralOp] = None,
43
+ init_scale: float = 1.0,
44
+ ) -> "Linear":
45
+ """
46
+ Args:
47
+ In: AxisSpec: The input axis spec
48
+ Out: AxisSpec: The output axis spec
49
+ key: PRNGKeyArray: The PRNG key to use for initialization
50
+ use_bias: bool: Whether to use a bias term
51
+ out_first: bool: Whether to put output axes first in the weight matrix. out_first is how PyTorch does it.
52
+ dot_general: Callable: The dot_general function to use. Defaults to jax.lax.dot_general.
53
+ init_scale: float: The scale to use for initialization. We scale init by 1/sqrt(Input.size)*init_scale
54
+ """
55
+ joint_spec = hax.concat_axis_specs(Out, In) if out_first else hax.concat_axis_specs(In, Out)
56
+ input_size = hax.axis_size(In)
57
+ weight = hax.random.truncated_normal(key, joint_spec, -3, 3) * (init_scale / math.sqrt(input_size))
58
+ bias = hax.zeros(Out) if use_bias else None
59
+
60
+ if dot_general is None:
61
+ dot_general = DotGeneralOp.default()
62
+
63
+ return Linear(weight, bias, In, Out, dot_general=dot_general)
64
+
65
+ @named_call
66
+ def __call__(self, inputs, *, key: Optional[PRNGKey] = None):
67
+ """
68
+ Args:
69
+ inputs (NamedArray): Input array
70
+ key: Not used, but there for compat with other modules
71
+ """
72
+ del key
73
+ q = inputs.dot(self.weight, axis=self.In, dot_general=self.dot_general)
74
+ q = hax.auto_sharded(q)
75
+
76
+ if self.bias is not None:
77
+ q = q + self.bias
78
+ q = hax.auto_sharded(q)
79
+
80
+ return q
81
+
82
+ def flatten_for_export(self: Mod) -> Mod:
83
+ if isinstance(self.Out, hax.Axis) and isinstance(self.In, hax.Axis):
84
+ return self
85
+
86
+ weight = self.weight
87
+ bias = self.bias
88
+
89
+ new_Out = hax.flatten_axes(self.Out, "__OUT__")
90
+ new_In = hax.flatten_axes(self.In, "__IN__")
91
+
92
+ if weight is not None and weight.array is not None:
93
+ out_first = self._out_first
94
+ weight = weight.flatten_axes(self.Out, new_Out).flatten_axes(self.In, new_In)
95
+
96
+ if out_first:
97
+ weight = weight.rearrange((..., "__OUT__", "__IN__"))
98
+ else:
99
+ weight = weight.rearrange((..., "__IN__", "__OUT__"))
100
+
101
+ if isinstance(bias, NamedArray):
102
+ bias = bias.flatten_axes(self.Out, new_Out)
103
+
104
+ return dataclasses.replace(self, weight=weight, bias=bias, In=new_In, Out=new_Out)
105
+
106
+ def unflatten_from_export(self: Mod, template: Mod) -> Mod:
107
+ weight = self.weight
108
+ bias = self.bias
109
+
110
+ if (template.In, template.Out) == (self.In, self.Out):
111
+ return self
112
+
113
+ if weight.array is not None:
114
+ weight = weight.unflatten_axis("__OUT__", template.Out).unflatten_axis("__IN__", template.In)
115
+ weight = weight.rearrange(template.weight.axes)
116
+
117
+ if isinstance(bias, NamedArray):
118
+ bias = bias.unflatten_axis("__OUT__", template.Out)
119
+ bias = bias.rearrange(template.bias.axes)
120
+
121
+ return dataclasses.replace(template, weight=weight, bias=bias)
122
+
123
+ @property
124
+ def _out_first(self):
125
+ """
126
+ Returns: bool: Whether the output axes are first in the weight matrix
127
+ """
128
+ # We do it this way because of scan layers
129
+ if isinstance(self.Out, hax.Axis):
130
+ return self.weight.axes[-1] != self.Out
131
+ else:
132
+ return self.weight.axes[-len(self.Out) :] != self.Out
133
+
134
+
135
+ class MoELinear(eqx.Module):
136
+ """A named Linear layer for MoE. This module allows you to specify multiple named axes for both input
137
+ and output, which is occasionally useful."""
138
+
139
+ weight: NamedArray
140
+ bias: Optional[NamedArray]
141
+
142
+ Experts: AxisSpec = eqx.static_field()
143
+ In: Axis = eqx.static_field()
144
+ Out: Axis = eqx.static_field()
145
+ dot_general: DotGeneralOp = eqx.field(default_factory=DotGeneralOp.default)
146
+
147
+ @staticmethod
148
+ def init(
149
+ Experts: Axis,
150
+ In: Axis,
151
+ Out: Axis,
152
+ *,
153
+ key: PRNGKey,
154
+ use_bias: bool = True,
155
+ out_first: bool = False,
156
+ init_scale: float = 1.0,
157
+ ) -> "MoELinear":
158
+ """
159
+ Args:
160
+ Experts: Axis: The expert axis
161
+ In: Axis: The input axis
162
+ Out: Axis: The output axis
163
+ key: PRNGKeyArray: The PRNG key to use for initialization
164
+ use_bias: bool: Whether to use a bias term
165
+ out_first: bool: Whether to put output axes first in the weight matrix. out_first is how PyTorch does it.
166
+ dot_general: Callable: The dot_general function to use. Defaults to jax.lax.dot_general. For fp8 or int8
167
+ init_scale: float: The scale to use for initialization. We scale init by 1/sqrt(Input.size)*init_scale
168
+ """
169
+ joint_spec = hax.concat_axis_specs(Out, In) if out_first else hax.concat_axis_specs(In, Out)
170
+ joint_spec = hax.concat_axis_specs(Experts, joint_spec)
171
+ input_size = hax.axis_size(In)
172
+ weight = hax.random.truncated_normal(key, joint_spec, -3, 3) * (init_scale / math.sqrt(input_size))
173
+ bias = hax.zeros(Out) if use_bias else None
174
+
175
+ return MoELinear(weight, bias, Experts, In, Out)
176
+
177
+ @named_call
178
+ def __call__(self, inputs, group_sizes, *, key: Optional[PRNGKey] = None):
179
+ """
180
+ Args:
181
+ inputs (NamedArray): Input array (Batch, In)
182
+ group_sizes (NamedArray): MoE expert sizes (Experts)
183
+ key: Not used, but there for compat with other modules
184
+ """
185
+ del key
186
+
187
+ inputs = inputs.rearrange((..., self.In))
188
+ out_axes = hax.replace_axis(inputs.axes, self.In, self.Out)
189
+
190
+ q = _gmm(
191
+ inputs,
192
+ self.weight,
193
+ group_sizes,
194
+ out_axes,
195
+ ar=hax.partitioning.physical_axis_name(self.In) == ResourceAxis.MODEL,
196
+ ) # gmm((B, D), (E, D, d)) -> (B, d)
197
+ q = hax.auto_sharded(q)
198
+
199
+ if self.bias is not None:
200
+ q = q + self.bias
201
+ q = hax.auto_sharded(q)
202
+
203
+ return q
204
+
205
+ @property
206
+ def out_first(self):
207
+ """
208
+ Returns: bool: Whether the output axes are first in the weight matrix
209
+ """
210
+ # We do it this way because of scan layers
211
+ if isinstance(self.Out, hax.Axis):
212
+ return self.weight.axes[-1] != self.Out
213
+ else:
214
+ return self.weight.axes[-len(self.Out) :] != self.Out
215
+
216
+
217
+ def _gmm(lhs, rhs, group_sizes, out_axes, sharded=False, ar=False):
218
+ if sharded:
219
+ gmm_fn = gmm_sharded
220
+ else:
221
+ gmm_fn = shard_map(
222
+ partial(gmm_sharded, ar=ar),
223
+ mesh=hax.partitioning._get_mesh(),
224
+ in_specs=(
225
+ hax.partitioning.pspec_for_axis(lhs.axes),
226
+ hax.partitioning.pspec_for_axis(rhs.axes),
227
+ hax.partitioning.pspec_for_axis(group_sizes.axes),
228
+ ),
229
+ out_specs=hax.partitioning.pspec_for_axis(out_axes),
230
+ check_rep=False,
231
+ )
232
+
233
+ out = gmm_fn(lhs.array, rhs.array, group_sizes.array)
234
+
235
+ return hax.NamedArray(out, axes=out_axes)
236
+
237
+
238
+ def gmm_sharded(lhs_: jnp.ndarray, rhs_: jnp.ndarray, group_sizes_: jnp.ndarray, ar: bool = False) -> jnp.ndarray:
239
+ hs_shape = lhs_.shape
240
+ if hs_shape[0] % 512:
241
+ pad_length = 512 - hs_shape[0] % 512
242
+ lhs_ = jax.lax.pad(lhs_, 0.0, [(0, pad_length, 0), (0, 0, 0)])
243
+
244
+ tile_size = (512, 1024, 1024) # (m, k, n)
245
+ m, k, n = lhs_.shape[0], lhs_.shape[1], rhs_.shape[2]
246
+ out = gmm(
247
+ lhs_,
248
+ rhs_,
249
+ group_sizes_,
250
+ preferred_element_type=lhs_.dtype,
251
+ tiling=(min(m, tile_size[0]), min(k, tile_size[1]), min(n, tile_size[2])),
252
+ # interpret=True,
253
+ )
254
+
255
+ if ar:
256
+ out = jax.lax.psum(out, ResourceAxis.MODEL)
257
+
258
+ if hs_shape[0] % 512:
259
+ out = out[: hs_shape[0]]
260
+
261
+ return out
@@ -1 +0,0 @@
1
- __version__ = "1.4.dev346"
@@ -1,126 +0,0 @@
1
- import dataclasses
2
- import math
3
- from typing import Optional
4
-
5
- import equinox as eqx
6
- from jax.random import PRNGKey
7
-
8
- import haliax as hax
9
-
10
- from .._src.state_dict import Mod, ModuleWithStateDictSerialization
11
- from ..axis import AxisSpec
12
- from ..core import NamedArray
13
- from ..jax_utils import named_call
14
- from ..quantization import DotGeneralOp
15
-
16
-
17
- class Linear(ModuleWithStateDictSerialization):
18
- """A named Linear layer. This module allows you to specify multiple named axes for both input
19
- and output, which is occasionally useful."""
20
-
21
- weight: NamedArray
22
- bias: Optional[NamedArray]
23
-
24
- In: AxisSpec = eqx.static_field()
25
- Out: AxisSpec = eqx.static_field()
26
- dot_general: DotGeneralOp = eqx.field(default_factory=DotGeneralOp.default)
27
-
28
- @staticmethod
29
- def init(
30
- In: AxisSpec,
31
- Out: AxisSpec,
32
- *,
33
- key: PRNGKey,
34
- use_bias: bool = True,
35
- out_first: bool = True,
36
- dot_general: Optional[DotGeneralOp] = None,
37
- init_scale: float = 1.0,
38
- ) -> "Linear":
39
- """
40
- Args:
41
- In: AxisSpec: The input axis spec
42
- Out: AxisSpec: The output axis spec
43
- key: PRNGKeyArray: The PRNG key to use for initialization
44
- use_bias: bool: Whether to use a bias term
45
- out_first: bool: Whether to put output axes first in the weight matrix. out_first is how PyTorch does it.
46
- dot_general: Callable: The dot_general function to use. Defaults to jax.lax.dot_general.
47
- init_scale: float: The scale to use for initialization. We scale init by 1/sqrt(Input.size)*init_scale
48
- """
49
- joint_spec = hax.concat_axis_specs(Out, In) if out_first else hax.concat_axis_specs(In, Out)
50
- input_size = hax.axis_size(In)
51
- weight = hax.random.truncated_normal(key, joint_spec, -3, 3) * (init_scale / math.sqrt(input_size))
52
- bias = hax.zeros(Out) if use_bias else None
53
-
54
- if dot_general is None:
55
- dot_general = DotGeneralOp.default()
56
-
57
- return Linear(weight, bias, In, Out, dot_general=dot_general)
58
-
59
- @named_call
60
- def __call__(self, inputs, *, key: Optional[PRNGKey] = None):
61
- """
62
- Args:
63
- inputs (NamedArray): Input array
64
- key: Not used, but there for compat with other modules
65
- """
66
- del key
67
- q = inputs.dot(self.weight, axis=self.In, dot_general=self.dot_general)
68
- q = hax.auto_sharded(q)
69
-
70
- if self.bias is not None:
71
- q = q + self.bias
72
- q = hax.auto_sharded(q)
73
-
74
- return q
75
-
76
- def flatten_for_export(self: Mod) -> Mod:
77
- if isinstance(self.Out, hax.Axis) and isinstance(self.In, hax.Axis):
78
- return self
79
-
80
- weight = self.weight
81
- bias = self.bias
82
-
83
- new_Out = hax.flatten_axes(self.Out, "__OUT__")
84
- new_In = hax.flatten_axes(self.In, "__IN__")
85
-
86
- if weight is not None and weight.array is not None:
87
- out_first = self._out_first
88
- weight = weight.flatten_axes(self.Out, new_Out).flatten_axes(self.In, new_In)
89
-
90
- if out_first:
91
- weight = weight.rearrange((..., "__OUT__", "__IN__"))
92
- else:
93
- weight = weight.rearrange((..., "__IN__", "__OUT__"))
94
-
95
- if isinstance(bias, NamedArray):
96
- bias = bias.flatten_axes(self.Out, new_Out)
97
-
98
- return dataclasses.replace(self, weight=weight, bias=bias, In=new_In, Out=new_Out)
99
-
100
- def unflatten_from_export(self: Mod, template: Mod) -> Mod:
101
- weight = self.weight
102
- bias = self.bias
103
-
104
- if (template.In, template.Out) == (self.In, self.Out):
105
- return self
106
-
107
- if weight.array is not None:
108
- weight = weight.unflatten_axis("__OUT__", template.Out).unflatten_axis("__IN__", template.In)
109
- weight = weight.rearrange(template.weight.axes)
110
-
111
- if isinstance(bias, NamedArray):
112
- bias = bias.unflatten_axis("__OUT__", template.Out)
113
- bias = bias.rearrange(template.bias.axes)
114
-
115
- return dataclasses.replace(template, weight=weight, bias=bias)
116
-
117
- @property
118
- def _out_first(self):
119
- """
120
- Returns: bool: Whether the output axes are first in the weight matrix
121
- """
122
- # We do it this way because of scan layers
123
- if isinstance(self.Out, hax.Axis):
124
- return self.weight.axes[-1] != self.Out
125
- else:
126
- return self.weight.axes[-len(self.Out) :] != self.Out
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