Skip to content

Commit 85a8181

Browse files
committed
Test math mode via __FAST_MATH__ instead of exp(-inf)
exp(-inf) agrees across math modes, so the old test passed regardless of the selected mode. Branch on the Metal compiler's __FAST_MATH__ macro instead, which is only defined under fast math, so the test fails if the math mode is not forwarded to the compiler. Reuse one kernel name across modes to exercise the library-cache rebuild path.
1 parent 7477e99 commit 85a8181

1 file changed

Lines changed: 41 additions & 21 deletions

File tree

python/tests/test_fast.py

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,27 +1047,47 @@ def test_custom_metal_kernel_invalid_compile_options(self):
10471047
)
10481048

10491049
@unittest.skipIf(not mx.metal.is_available(), "Metal is not available")
1050-
def test_custom_metal_kernel_safe_math_mode(self):
1051-
kernel = mx.fast.metal_kernel(
1052-
name="safe_math_mode",
1053-
input_names=["inp"],
1054-
output_names=["out"],
1055-
source="""
1056-
uint elem = thread_position_in_grid.x;
1057-
out[elem] = metal::exp(inp[elem]);
1058-
""",
1059-
compile_options={"math_mode": "safe"},
1060-
)
1061-
a = mx.array([-float("inf"), 0.0], dtype=mx.float32)
1062-
out = kernel(
1063-
inputs=[a],
1064-
grid=(a.size, 1, 1),
1065-
threadgroup=(a.size, 1, 1),
1066-
output_shapes=[a.shape],
1067-
output_dtypes=[a.dtype],
1068-
stream=mx.gpu,
1069-
)[0]
1070-
self.assertTrue(mx.array_equal(out, mx.array([0.0, 1.0])))
1050+
def test_custom_metal_kernel_math_mode(self):
1051+
# Numerical special cases such as exp(-inf) can agree between math
1052+
# modes, so they don't reliably detect whether the mode was applied.
1053+
# Branch on the compiler's __FAST_MATH__ macro instead: it is defined
1054+
# only when fast math is enabled, so the test fails if the selected
1055+
# math mode is not forwarded to the Metal compiler.
1056+
source = """
1057+
uint elem = thread_position_in_grid.x;
1058+
#if defined(__FAST_MATH__) && __FAST_MATH__
1059+
out[elem] = 1.0f;
1060+
#else
1061+
out[elem] = 0.0f;
1062+
#endif
1063+
"""
1064+
1065+
a = mx.zeros((4,), dtype=mx.float32)
1066+
expected = {
1067+
"safe": mx.zeros_like(a),
1068+
"fast": mx.ones_like(a),
1069+
}
1070+
1071+
# Reuse the same kernel name across modes so the library cache is forced
1072+
# to rebuild when the math mode changes, guarding against a stale build
1073+
# being returned for a different mode.
1074+
for mode, expected_out in expected.items():
1075+
kernel = mx.fast.metal_kernel(
1076+
name="math_mode",
1077+
input_names=["inp"],
1078+
output_names=["out"],
1079+
source=source,
1080+
compile_options={"math_mode": mode},
1081+
)
1082+
out = kernel(
1083+
inputs=[a],
1084+
grid=(a.size, 1, 1),
1085+
threadgroup=(a.size, 1, 1),
1086+
output_shapes=[a.shape],
1087+
output_dtypes=[a.dtype],
1088+
stream=mx.gpu,
1089+
)[0]
1090+
self.assertTrue(mx.array_equal(out, expected_out))
10711091

10721092
@unittest.skipIf(not mx.metal.is_available(), "Metal is not available")
10731093
def test_custom_kernel_mixed_dtypes(self):

0 commit comments

Comments
 (0)