@@ -166,10 +166,11 @@ def atan2_impl(x1: Var, x2: Var) -> Var:
166166@impl (ct .minimum , fixed_args = ["min" ])
167167@impl (ct .maximum , fixed_args = ["max" ])
168168def tile_binary_arithmetic_function_impl_with_ftz (fn : str , x : Var , y : Var ,
169- flush_to_zero : Var ) -> Var :
169+ flush_to_zero : Var , propagate_nan : Var ) -> Var :
170170 flush_to_zero = require_constant_bool (flush_to_zero )
171+ propagate_nan = require_constant_bool (propagate_nan )
171172 return binary_arithmetic_tensorlike (fn , ensure_tile (x ), ensure_tile (y ),
172- flush_to_zero = flush_to_zero )
173+ flush_to_zero = flush_to_zero , propagate_nan = propagate_nan )
173174
174175
175176@impl (ct .add , fixed_args = ["add" ])
@@ -2331,7 +2332,7 @@ def _get_reduction_shape(shape: Tuple[int, ...],
23312332
23322333async def reduce_simple (fn : str , x : Var , axis : int | None | tuple [int , ...], keepdims : bool ,
23332334 rounding_mode : Optional [RoundingMode ] = None ,
2334- flush_to_zero : bool = False ) -> Var :
2335+ flush_to_zero : bool = False , propagate_nan : bool = False ) -> Var :
23352336 x_type = require_tile_type (x )
23362337 if not datatype .is_arithmetic (x_type .dtype ):
23372338 raise TileTypeError (f"Non-arithmetic dtype { x_type .dtype } is unsupported for reduction" )
@@ -2351,7 +2352,8 @@ async def reduce_simple(fn: str, x: Var, axis: int | None | tuple[int, ...], kee
23512352 async def body (lhs : tuple [Var ], rhs : tuple [Var ]) -> tuple [Var ]:
23522353 [lhs ], [rhs ] = lhs , rhs
23532354 ret = binary_arithmetic_tensorlike (fn , lhs , rhs ,
2354- rounding_mode = rounding_mode , flush_to_zero = flush_to_zero )
2355+ rounding_mode = rounding_mode , flush_to_zero = flush_to_zero ,
2356+ propagate_nan = propagate_nan )
23552357 return (ret ,)
23562358
23572359 [ret ] = await reduce ((x ,), (id_val ,), axis , keepdims , body )
@@ -2399,14 +2401,17 @@ async def reduce_impl_with_rd_and_ftz(fn: str, x: Var, axis: Var, keepdims: Var,
23992401@impl (ct .max , fixed_args = ["max" ])
24002402@impl (ct .min , fixed_args = ["min" ])
24012403async def reduce_impl_with_ftz (fn : str , x : Var , axis : Var , keepdims : Var ,
2402- flush_to_zero : Var ) -> Var :
2404+ flush_to_zero : Var , propagate_nan : Var ) -> Var :
24032405 axis = _parse_reduce_axis (axis )
24042406 keepdims = require_constant_bool (keepdims )
24052407 flush_to_zero = require_constant_bool (flush_to_zero )
2406- return await reduce_simple (fn , x , axis , keepdims , flush_to_zero = flush_to_zero )
2408+ propagate_nan = require_constant_bool (propagate_nan )
2409+ return await reduce_simple (fn , x , axis , keepdims , flush_to_zero = flush_to_zero ,
2410+ propagate_nan = propagate_nan )
24072411
24082412
2409- async def argmax_argmin (fn : str , x : Var , axis : Optional [int ], keepdims : bool ) -> Var :
2413+ async def argmax_argmin (fn : str , x : Var , axis : Optional [int ], keepdims : bool ,
2414+ propagate_nan : bool = False ) -> Var :
24102415 require_tile_type (x )
24112416 final_shape = None
24122417 if axis is None :
@@ -2434,14 +2439,34 @@ async def argmax_argmin(fn: str, x: Var, axis: Optional[int], keepdims: bool) ->
24342439 cmp = "gt"
24352440 case _: assert False
24362441
2442+ is_float_dtype = datatype .is_float (x_type .dtype )
2443+
24372444 async def body (lhs : tuple [Var , Var ], rhs : tuple [Var , Var ]) -> tuple [Var , Var ]:
24382445 lhs_val , lhs_idx = lhs
24392446 rhs_val , rhs_idx = rhs
2440- val_strict = compare_tensorlike_raw (cmp , lhs_val , rhs_val )
2447+ lhs_win = compare_tensorlike_raw (cmp , lhs_val , rhs_val )
24412448 val_equal = compare_tensorlike_raw ("eq" , lhs_val , rhs_val )
2449+ if is_float_dtype :
2450+ lhs_is_nan = compare_tensorlike_raw ("ne" , lhs_val , lhs_val )
2451+ rhs_is_nan = compare_tensorlike_raw ("ne" , rhs_val , rhs_val )
2452+ if propagate_nan :
2453+ # Mirror min/max's propagate_nan=True semantics by
2454+ # treating NaN as the best possible value.
2455+ rhs_not_nan = compare_tensorlike_raw ("eq" , rhs_val , rhs_val )
2456+ lhs_nan_rhs_finite = binary_bitwise_tensorlike_raw ("and_" , lhs_is_nan , rhs_not_nan )
2457+ lhs_win = binary_bitwise_tensorlike_raw ("or_" , lhs_win , lhs_nan_rhs_finite )
2458+ else :
2459+ # Mirror min/max's propagate_nan=False semantics by
2460+ # treating NaN as the worst possible value.
2461+ lhs_not_nan = compare_tensorlike_raw ("eq" , lhs_val , lhs_val )
2462+ lhs_finite_rhs_nan = binary_bitwise_tensorlike_raw ("and_" , lhs_not_nan , rhs_is_nan )
2463+ lhs_win = binary_bitwise_tensorlike_raw ("or_" , lhs_win , lhs_finite_rhs_nan )
2464+ # two NaNs count as "equal" so the index tiebreak (smallest index) decides.
2465+ both_nan = binary_bitwise_tensorlike_raw ("and_" , lhs_is_nan , rhs_is_nan )
2466+ val_equal = binary_bitwise_tensorlike_raw ("or_" , val_equal , both_nan )
24422467 index_lt = compare_tensorlike_raw ("lt" , lhs_idx , rhs_idx )
24432468 val_equal_and_index_lt = binary_bitwise_tensorlike_raw ("and_" , val_equal , index_lt )
2444- cond = binary_bitwise_tensorlike_raw ("or_" , val_strict , val_equal_and_index_lt )
2469+ cond = binary_bitwise_tensorlike_raw ("or_" , lhs_win , val_equal_and_index_lt )
24452470 res = where_raw (cond , lhs_val , rhs_val )
24462471 idx = where_raw (cond , lhs_idx , rhs_idx )
24472472 return res , idx
@@ -2456,10 +2481,11 @@ async def body(lhs: tuple[Var, Var], rhs: tuple[Var, Var]) -> tuple[Var, Var]:
24562481
24572482@impl (ct .argmax , fixed_args = ["argmax" ])
24582483@impl (ct .argmin , fixed_args = ["argmin" ])
2459- async def argmax_argmin_impl (fn : str , x : Var , axis : Var , keepdims : Var ) -> Var :
2484+ async def argmax_argmin_impl (fn : str , x : Var , axis : Var , keepdims : Var , propagate_nan : Var ) -> Var :
24602485 axis = require_optional_constant_int (axis )
24612486 keepdims = require_constant_bool (keepdims )
2462- return await argmax_argmin (fn , x , axis , keepdims )
2487+ propagate_nan = require_constant_bool (propagate_nan )
2488+ return await argmax_argmin (fn , x , axis , keepdims , propagate_nan = propagate_nan )
24632489
24642490
24652491@dataclass (eq = False )
0 commit comments