Skip to content

Commit 50e041a

Browse files
committed
Revert "[JAX] expand math_dtype argument (#153)"
This reverts commit 9b6788b.
1 parent bb648e2 commit 50e041a

10 files changed

Lines changed: 216 additions & 180 deletions

cuequivariance_jax/cuequivariance_jax/equivariant_polynomial.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ def equivariant_polynomial(
2929
indices: None | list[None | jax.Array | tuple[jax.Array | slice]] = None,
3030
*,
3131
method: str = "",
32-
math_dtype: str | None = None,
32+
math_dtype: jnp.dtype | None = None,
3333
name: str | None = None,
34-
precision: jax.lax.Precision = "undefined",
34+
precision: jax.lax.Precision = jax.lax.Precision.HIGHEST,
3535
) -> list[cuex.RepArray] | cuex.RepArray:
3636
"""Compute an equivariant polynomial.
3737
@@ -50,8 +50,10 @@ def equivariant_polynomial(
5050
total number of operands (inputs + outputs). Use None for unindexed
5151
operands. Defaults to None. Note that indices are not supported for all methods.
5252
method: Method to use for computation. See :func:`cuex.segmented_polynomial <cuequivariance_jax.segmented_polynomial>` for available methods.
53-
math_dtype: See :func:`cuex.segmented_polynomial <cuequivariance_jax.segmented_polynomial>` for supported options.
53+
math_dtype: Data type for computational operations. If None, automatically
54+
determined from input types. Defaults to None.
5455
name: Optional name for the operation. Defaults to None.
56+
precision: The precision to use for the computation. Defaults to HIGHEST. Note that precision is not supported for all methods.
5557
5658
Returns:
5759
:class:`cuex.RepArray <cuequivariance_jax.RepArray>` or list of :class:`cuex.RepArray <cuequivariance_jax.RepArray>`
@@ -102,11 +104,6 @@ def equivariant_polynomial(
102104
if name is None:
103105
name = "equivariant_polynomial"
104106

105-
if precision != "undefined":
106-
raise ValueError(
107-
"precision is not anymore supported. Please use math_dtype instead."
108-
)
109-
110107
if len(inputs) != poly.num_inputs:
111108
raise ValueError(
112109
f"Unexpected number of inputs. Expected {poly.num_inputs}, got {len(inputs)}."
@@ -188,6 +185,7 @@ def equivariant_polynomial(
188185
math_dtype=math_dtype,
189186
name=name,
190187
method=method,
188+
precision=precision,
191189
)
192190
outputs = [cuex.RepArray(rep, x) for rep, x in zip(poly.outputs, outputs)]
193191

cuequivariance_jax/cuequivariance_jax/experimental/indexed_linear.py

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,84 @@
1414
# limitations under the License.
1515
import jax
1616
import jax.lax
17+
import jax.numpy as jnp
1718

1819
import cuequivariance as cue
20+
import cuequivariance_jax as cuex
1921

2022

2123
def indexed_linear(
2224
poly: cue.SegmentedPolynomial,
2325
counts: jax.Array,
2426
w: jax.Array,
2527
x: jax.Array,
26-
math_dtype: str | None = None,
28+
math_dtype: jnp.dtype | None = None,
2729
method: str = "indexed_linear",
2830
) -> jax.Array:
29-
raise NotImplementedError(
30-
"Use cuex.segmented_polynomial(..., method='indexed_linear') instead."
31+
"""Linear layer with different weights for different parts of the input.
32+
33+
Args:
34+
poly: The polynomial descriptor. Only works for descriptors of a linear layer.
35+
counts: Number of elements in each partition. Shape (C,).
36+
w: Weights of the linear layer. Shape (C, num_weights).
37+
x: Input data. Shape (Z, num_inputs). Z is equal to the sum of counts.
38+
math_dtype: Data type for computational operations. If
39+
None, automatically determined from input types. Defaults to None.
40+
Returns:
41+
Output data. Shape (Z, num_outputs).
42+
43+
Examples:
44+
This example demonstrates using indexed_linear for a batch of inputs with
45+
different species:
46+
47+
>>> import jax
48+
>>> import jax.numpy as jnp
49+
>>> import cuequivariance as cue
50+
>>> import cuequivariance_jax as cuex
51+
>>>
52+
>>> # Define problem parameters
53+
>>> num_species_total = 3 # Total number of different species
54+
>>> batch_size = 10 # Number of samples in batch
55+
>>> input_dim = 8 # Input feature dimension
56+
>>> output_dim = 16 # Output feature dimension
57+
>>> dtype = jnp.float32
58+
>>>
59+
>>> # Define how many elements belong to each species
60+
>>> num_species = jnp.array([3, 4, 3], dtype=jnp.int32) # Sum equals batch_size
61+
>>>
62+
>>> # Generate random input data
63+
>>> input_array = jax.random.normal(jax.random.key(0), (batch_size, input_dim), dtype)
64+
>>>
65+
>>> # Define irreps for input and output features
66+
>>> input_irreps = cue.Irreps(cue.O3, f"{input_dim}x0e") # Scalar features
67+
>>> output_irreps = cue.Irreps(cue.O3, f"{output_dim}x0e") # Scalar features
68+
>>>
69+
>>> # Create a linear descriptor
70+
>>> e = cue.descriptors.linear(input_irreps, output_irreps)
71+
>>>
72+
>>> # Generate weights for each species
73+
>>> w = jax.random.normal(jax.random.key(1), (num_species_total, e.inputs[0].dim), dtype)
74+
>>>
75+
>>> # Apply the indexed linear layer
76+
>>> result = cuex.experimental.indexed_linear(e.polynomial, num_species, w, input_array)
77+
>>>
78+
>>> # Verify output shape
79+
>>> assert result.shape == (batch_size, output_dim)
80+
"""
81+
assert poly.num_inputs == 2
82+
assert poly.num_outputs == 1
83+
84+
(C, _) = w.shape
85+
(Z, _) = x.shape
86+
assert counts.shape == (C,)
87+
88+
y = jax.ShapeDtypeStruct((Z, poly.outputs[0].size), x.dtype)
89+
[y] = cuex.segmented_polynomial(
90+
poly,
91+
[w, x],
92+
[y],
93+
[cuex.Repeats(counts), None, None],
94+
math_dtype=math_dtype,
95+
method=method,
3196
)
97+
return y

cuequivariance_jax/cuequivariance_jax/segmented_polynomials/segmented_polynomial.py

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ def segmented_polynomial(
5454
indices: None | list[None | jax.Array | tuple[jax.Array | slice]] = None,
5555
*,
5656
method: str = "",
57-
math_dtype: str | None = None,
57+
math_dtype: jnp.dtype | None = None,
5858
name: str | None = None,
59-
precision: jax.lax.Precision = "undefined",
59+
precision: jax.lax.Precision = jax.lax.Precision.HIGHEST,
6060
) -> list[jax.Array]:
6161
"""Compute a segmented polynomial.
6262
@@ -79,17 +79,13 @@ def segmented_polynomial(
7979
8080
.. note::
8181
The ``"fused_tp"`` method is only available in the PyTorch implementation.
82-
math_dtype: Data type for computational operations. If None, automatically determined from input types. Defaults to None.
83-
84-
Supported options vary by method:
85-
86-
- ``"naive"``: String dtype names (e.g., ``"float32"``, ``"float64"``, ``"float16"``, ``"bfloat16"``).
87-
Also supports ``"tensor_float32"`` for TensorFloat-32 mode.
88-
- ``"uniform_1d"``: String values ``"float32"`` or ``"float64"`` only.
89-
- ``"indexed_linear"``: CUBLAS compute type strings such as ``"CUBLAS_COMPUTE_32F"``, ``"CUBLAS_COMPUTE_32F_FAST_TF32"``,
90-
``"CUBLAS_COMPUTE_32F_PEDANTIC"``, ``"CUBLAS_COMPUTE_64F"``, etc.
82+
math_dtype: Data type for computational operations. If None, automatically
83+
determined from input types, defaulting to float32 if no float64 inputs
84+
are present.
9185
9286
name: Optional name for the operation.
87+
precision: The precision to use for the computation. Defaults to HIGHEST.
88+
Note that precision is only supported for the ``"naive"`` method.
9389
9490
Returns:
9591
List of JAX arrays containing the computed polynomial outputs.
@@ -163,15 +159,6 @@ def segmented_polynomial(
163159
if name is None:
164160
name = "segmented_polynomial"
165161

166-
if math_dtype is not None and not isinstance(math_dtype, str):
167-
math_dtype = jnp.dtype(math_dtype).name
168-
assert isinstance(math_dtype, str) or math_dtype is None
169-
170-
if precision != "undefined":
171-
raise ValueError(
172-
"precision is not anymore supported. Please use math_dtype instead."
173-
)
174-
175162
assert len(inputs) == polynomial.num_inputs
176163
assert len(outputs_shape_dtype) == polynomial.num_outputs
177164

@@ -281,6 +268,10 @@ def fn(x, n: int):
281268
index_configuration.append(bi)
282269
index_mode.append(im)
283270

271+
# Set default math_dtype
272+
if math_dtype is None:
273+
math_dtype = jnp.result_type(*io_buffers)
274+
284275
# Execute the polynomial
285276
kwargs = dict(
286277
inputs=io_buffers[: polynomial.num_inputs],
@@ -291,6 +282,7 @@ def fn(x, n: int):
291282
polynomial=polynomial,
292283
math_dtype=math_dtype,
293284
name=name,
285+
precision=precision,
294286
)
295287

296288
outputs = segmented_polynomial_prim(**kwargs, method=method)
@@ -352,9 +344,10 @@ def segmented_polynomial_prim(
352344
index_configuration: list[list[int]], # maps: buffer index -> unique indices index
353345
index_mode: list[list[IndexingMode]], # shared, batched, indexed, repeated
354346
polynomial: cue.SegmentedPolynomial,
355-
math_dtype: str | None,
347+
math_dtype: jnp.dtype,
356348
name: str,
357349
method: str,
350+
precision: jax.lax.Precision,
358351
return_none_if_empty: bool = False,
359352
) -> tuple[jax.Array, ...]: # output buffers
360353
"""
@@ -383,9 +376,10 @@ def segmented_polynomial_prim(
383376
x for x, used in zip(outputs_shape_dtype, used_outputs) if used
384377
),
385378
polynomial=polynomial.filter_keep_operands(used_inputs + used_outputs),
386-
math_dtype=math_dtype,
379+
math_dtype=jnp.dtype(math_dtype),
387380
name=str(name),
388381
method=method,
382+
precision=precision,
389383
)
390384

391385
if return_none_if_empty:
@@ -441,9 +435,10 @@ def segmented_polynomial_abstract_eval(
441435
index_mode: tuple[tuple[IndexingMode, ...], ...],
442436
outputs_shape_dtype: tuple[jax.ShapeDtypeStruct, ...],
443437
polynomial: cue.SegmentedPolynomial,
444-
math_dtype: str | None,
438+
math_dtype: jnp.dtype,
445439
name: str,
446440
method: str,
441+
precision: jax.lax.Precision,
447442
) -> tuple[jax.core.ShapedArray, ...]:
448443
return tuple(
449444
jax.core.ShapedArray(out.shape, out.dtype) for out in outputs_shape_dtype
@@ -457,9 +452,10 @@ def segmented_polynomial_impl(
457452
index_mode: tuple[tuple[IndexingMode, ...], ...],
458453
outputs_shape_dtype: tuple[jax.ShapeDtypeStruct, ...],
459454
polynomial: cue.SegmentedPolynomial,
460-
math_dtype: str | None,
455+
math_dtype: jnp.dtype,
461456
name: str,
462457
method: str,
458+
precision: jax.lax.Precision,
463459
) -> tuple[jax.Array, ...]:
464460
num_inputs = len(index_configuration) - len(outputs_shape_dtype)
465461
inputs, indices = inputs_and_indices[:num_inputs], inputs_and_indices[num_inputs:]
@@ -510,14 +506,21 @@ def segmented_polynomial_impl(
510506
raise ValueError(
511507
"IndexingMode.REPEATED is only supported with 'naive' or 'indexed_linear' methods."
512508
)
509+
if precision != jax.lax.Precision.HIGHEST:
510+
if method not in ("naive",):
511+
raise ValueError(
512+
f"Precision {precision} is only supported with 'naive' method."
513+
)
513514

514515
match method:
515516
case "naive":
516-
return execute_naive(**kwargs, index_mode=index_mode)
517+
return execute_naive(**kwargs, index_mode=index_mode, precision=precision)
517518
case "uniform_1d":
518519
return execute_uniform_1d(**kwargs)
519520
case "indexed_linear":
520-
return execute_indexed_linear(**kwargs, index_mode=index_mode)
521+
return execute_indexed_linear(
522+
**kwargs, index_mode=index_mode, precision=precision
523+
)
521524

522525

523526
def segmented_polynomial_jvp(
@@ -528,9 +531,10 @@ def segmented_polynomial_jvp(
528531
index_mode: tuple[tuple[IndexingMode, ...], ...],
529532
outputs_shape_dtype: tuple[jax.ShapeDtypeStruct, ...],
530533
polynomial: cue.SegmentedPolynomial,
531-
math_dtype: str | None,
534+
math_dtype: jnp.dtype,
532535
name: str,
533536
method: str,
537+
precision: jax.lax.Precision,
534538
) -> tuple[tuple[jax.Array, ...], tuple[jax.Array | ad.Zero, ...]]:
535539
num_inputs = len(index_configuration) - len(outputs_shape_dtype)
536540

@@ -552,6 +556,7 @@ def segmented_polynomial_jvp(
552556
math_dtype,
553557
name,
554558
method=method,
559+
precision=precision,
555560
)
556561

557562
jvp_poly, _ = polynomial.jvp([not isinstance(t, ad.Zero) for t in tangents])
@@ -576,6 +581,7 @@ def segmented_polynomial_jvp(
576581
+ "_jvp"
577582
+ "".join("0" if isinstance(t, ad.Zero) else "1" for t in tangents),
578583
method=method,
584+
precision=precision,
579585
)
580586

581587
return out_primals, out_tangents
@@ -588,9 +594,10 @@ def segmented_polynomial_transpose(
588594
index_mode: tuple[tuple[IndexingMode, ...], ...],
589595
outputs_shape_dtype: tuple[jax.ShapeDtypeStruct, ...],
590596
polynomial: cue.SegmentedPolynomial,
591-
math_dtype: str | None,
597+
math_dtype: jnp.dtype,
592598
name: str,
593599
method: str,
600+
precision: jax.lax.Precision,
594601
) -> tuple[jax.Array | ad.Zero | None, ...]:
595602
num_inputs = len(index_configuration) - len(outputs_shape_dtype)
596603
inputs, indices = inputs_and_indices[:num_inputs], inputs_and_indices[num_inputs:]
@@ -632,6 +639,7 @@ def segmented_polynomial_transpose(
632639
math_dtype,
633640
name + "_T",
634641
method=method,
642+
precision=precision,
635643
return_none_if_empty=True,
636644
)
637645

@@ -652,9 +660,10 @@ def segmented_polynomial_batching(
652660
index_mode: tuple[tuple[IndexingMode, ...], ...],
653661
outputs_shape_dtype: tuple[jax.ShapeDtypeStruct, ...],
654662
polynomial: cue.SegmentedPolynomial,
655-
math_dtype: str | None,
663+
math_dtype: jnp.dtype,
656664
name: str,
657665
method: str,
666+
precision: jax.lax.Precision,
658667
) -> tuple[tuple[jax.Array, ...], tuple[int, ...]]:
659668
# Add a new batch axis in the first dimension
660669
def prepare(input: jax.Array, axis: int | None) -> jax.Array:
@@ -695,6 +704,7 @@ def prepare(input: jax.Array, axis: int | None) -> jax.Array:
695704
math_dtype=math_dtype,
696705
name=name + "_batching",
697706
method=method,
707+
precision=precision,
698708
)
699709
return outputs, (0,) * len(outputs)
700710

0 commit comments

Comments
 (0)