/* * Copyright (c) Meta Platforms, Inc. and affiliates. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ #include #include #include #include #include #include namespace torch { namespace executor { namespace native { namespace { template constexpr CTYPE lower_bound() { using lim = std::numeric_limits; return lim::has_infinity ? -lim::infinity() : lim::lowest(); } } // namespace using ScalarType = executorch::aten::ScalarType; using SizesType = executorch::aten::SizesType; using Tensor = executorch::aten::Tensor; std::tuple max_out( KernelRuntimeContext& ctx, const Tensor& in, int64_t dim, bool keepdim, Tensor& max, Tensor& max_indices) { (void)ctx; ET_KERNEL_CHECK( ctx, check_min_max_args(in, dim, keepdim, max, max_indices), InvalidArgument, (std::tuple({max, max_indices}))); ET_KERNEL_CHECK( ctx, resize_reduction_out(in, dim, keepdim, max) == Error::Ok, InvalidArgument, (std::tuple({max, max_indices}))); ET_KERNEL_CHECK( ctx, resize_tensor(max_indices, max.sizes()) == Error::Ok, InvalidArgument, (std::tuple({max, max_indices}))); ET_KERNEL_CHECK( ctx, tensors_have_same_dim_order(in, max), InvalidArgument, (std::tuple({max, max_indices}))); ET_KERNEL_CHECK( ctx, tensor_is_default_dim_order(max_indices), InvalidArgument, (std::tuple({max, max_indices}))); ET_KERNEL_CHECK( ctx, tensor_is_default_dim_order(in), InvalidArgument, (std::tuple({max, max_indices}))); dim = dim < 0 ? dim + in.dim() : dim; ET_SWITCH_REAL_TYPES_AND( Bool, in.scalar_type(), ctx, "max.dim_max", CTYPE, [&]() { CTYPE* max_data = max.mutable_data_ptr(); long* max_indices_data = max_indices.mutable_data_ptr(); const bool success = parallel_for_each_reduce_over_dim_output_index( in, dim, max, [&](const auto begin, const auto end) { for (const auto out_ix : c10::irange(begin, end)) { std::tuple acc = reduce_over_dim( [](CTYPE v, long ix, CTYPE acc_val, long acc_ix) { if (!std::isnan(acc_val) && (std::isnan(v) || v > acc_val)) { acc_val = v; acc_ix = ix; } return std::tuple{acc_val, acc_ix}; }, in, dim, out_ix); max_data[out_ix] = std::get<0>(acc); max_indices_data[out_ix] = std::get<1>(acc); } }); ET_KERNEL_CHECK_MSG(ctx, success, Internal, , "parallel_for failed"); }); return {max, max_indices}; } Tensor& max_unary_out(KernelRuntimeContext& ctx, const Tensor& in, Tensor& out) { (void)ctx; ET_KERNEL_CHECK( ctx, resize_tensor(out, {}) == Error::Ok, InvalidArgument, out); ET_KERNEL_CHECK( ctx, tensors_have_same_dim_order(in, out), InvalidArgument, out); ScalarType in_type = in.scalar_type(); ScalarType out_type = out.scalar_type(); ET_KERNEL_CHECK(ctx, canCast(in_type, out_type), InvalidArgument, out); constexpr auto name = "max.unary_out"; ET_SWITCH_REALHBBF16_TYPES(in_type, ctx, name, CTYPE_IN, [&] { ET_SWITCH_REALHBBF16_TYPES(out_type, ctx, name, CTYPE_OUT, [&] { const auto data_in = in.const_data_ptr(); auto data_out = out.mutable_data_ptr(); data_out[0] = lower_bound(); for (const auto i : c10::irange(in.numel())) { CTYPE_OUT val = static_cast(data_in[i]); if (std::isnan(val)) { data_out[0] = val; break; } if (val > data_out[0]) { data_out[0] = val; } } }); }); return out; } } // namespace native } // namespace executor } // namespace torch