| 38 | } |
| 39 | |
| 40 | void Compute(OpKernelContext* context) override { |
| 41 | // The second args is N, which must be a positive scalar. |
| 42 | const auto& n_in = context->input(1); |
| 43 | OP_REQUIRES(context, TensorShapeUtils::IsScalar(n_in.shape()), |
| 44 | errors::InvalidArgument("N must be scalar, got shape ", |
| 45 | n_in.shape().DebugString())); |
| 46 | int n = n_in.scalar<int32>()(); |
| 47 | OP_REQUIRES(context, n >= 0, |
| 48 | errors::InvalidArgument("Need n >= 0, got ", n)); |
| 49 | |
| 50 | // The first args is input tensor, which must have 1 dimension at least. |
| 51 | const Tensor& input_in = context->input(0); |
| 52 | const int num_dims = input_in.dims(); |
| 53 | OP_REQUIRES(context, num_dims >= 1, |
| 54 | errors::InvalidArgument("Input must be >= 1-D, got shape ", |
| 55 | input_in.shape().DebugString())); |
| 56 | // The last dimension of input tensor must be greater than N. |
| 57 | OP_REQUIRES( |
| 58 | context, input_in.dim_size(num_dims - 1) > n, |
| 59 | errors::InvalidArgument("Input must have at least n+1 columns")); |
| 60 | |
| 61 | // std::nth_element only support the nth-smallest selection. |
| 62 | if (reverse_) { |
| 63 | n = input_in.dim_size(num_dims - 1) - n - 1; |
| 64 | } |
| 65 | |
| 66 | // Assume input_shape is [d1,d2,...dk], and output_shape is [d1,d2...dk-1]. |
| 67 | TensorShape out_shape; |
| 68 | for (int i = 0; i < num_dims - 1; ++i) { |
| 69 | out_shape.AddDim(input_in.dim_size(i)); |
| 70 | } |
| 71 | Tensor* output_tensor = nullptr; |
| 72 | OP_REQUIRES_OK(context, |
| 73 | context->allocate_output(0, out_shape, &output_tensor)); |
| 74 | |
| 75 | functor::NthElementFunctor<Device, T> nthElementFunc; |
| 76 | nthElementFunc(context, input_in, *output_tensor, n, reverse_); |
| 77 | } |
| 78 | |
| 79 | private: |
| 80 | bool reverse_; |
nothing calls this directly
no test coverage detected