Home | History | Annotate | Download | only in kernels
      1 /* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
      2 
      3 Licensed under the Apache License, Version 2.0 (the "License");
      4 you may not use this file except in compliance with the License.
      5 You may obtain a copy of the License at
      6 
      7     http://www.apache.org/licenses/LICENSE-2.0
      8 
      9 Unless required by applicable law or agreed to in writing, software
     10 distributed under the License is distributed on an "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 See the License for the specific language governing permissions and
     13 limitations under the License.
     14 ==============================================================================*/
     15 
     16 #include <vector>
     17 
     18 #include "tensorflow/compiler/tf2xla/shape_util.h"
     19 #include "tensorflow/compiler/tf2xla/type_util.h"
     20 #include "tensorflow/compiler/tf2xla/xla_helpers.h"
     21 #include "tensorflow/compiler/tf2xla/xla_op_kernel.h"
     22 #include "tensorflow/compiler/tf2xla/xla_op_registry.h"
     23 #include "tensorflow/compiler/xla/literal_util.h"
     24 #include "tensorflow/core/framework/op_kernel.h"
     25 #include "tensorflow/core/framework/partial_tensor_shape.h"
     26 #include "tensorflow/core/framework/register_types.h"
     27 #include "tensorflow/core/framework/tensor.h"
     28 #include "tensorflow/core/framework/tensor_types.h"
     29 #include "tensorflow/core/framework/types.h"
     30 #include "tensorflow/core/kernels/bounds_check.h"
     31 #include "tensorflow/core/kernels/concat_lib.h"
     32 #include "tensorflow/core/lib/core/status.h"
     33 #include "tensorflow/core/platform/types.h"
     34 
     35 namespace tensorflow {
     36 namespace {
     37 
     38 // TODO(phawkins): implement double-sized windowed reductions in XLA and remove
     39 // the type constraint.
     40 constexpr std::array<DataType, 3> kScanOpTypes = {
     41     {DT_HALF, DT_BFLOAT16, DT_FLOAT}};
     42 
     43 class ScanOp : public XlaOpKernel {
     44  public:
     45   ScanOp(OpKernelConstruction* ctx, bool sum) : XlaOpKernel(ctx), sum_(sum) {
     46     OP_REQUIRES_OK(ctx, ctx->GetAttr("reverse", &reverse_));
     47     OP_REQUIRES_OK(ctx, ctx->GetAttr("exclusive", &exclusive_));
     48   }
     49 
     50   void Compile(XlaOpKernelContext* ctx) override {
     51     const TensorShape input_shape = ctx->InputShape(0);
     52     const TensorShape tensor_axis_shape = ctx->InputShape(1);
     53 
     54     OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(tensor_axis_shape),
     55                 errors::InvalidArgument("ScanOp: axis must be a scalar, not ",
     56                                         tensor_axis_shape.DebugString()));
     57 
     58     int64 axis;
     59     OP_REQUIRES_OK(ctx, ctx->ConstantInputAsIntScalar(1, &axis));
     60     if (axis < 0) {
     61       axis += input_shape.dims();
     62     }
     63     OP_REQUIRES(
     64         ctx, FastBoundsCheck(axis, input_shape.dims()),
     65         errors::InvalidArgument("ScanOp: Expected scan axis in the range [",
     66                                 -input_shape.dims(), ", ", input_shape.dims(),
     67                                 "), but got ", axis));
     68 
     69     DataType dtype = ctx->input_type(0);
     70 
     71     if (input_shape.num_elements() == 0) {
     72       // Exit early if there is nothing to compute.
     73       ctx->SetOutput(0, ctx->Input(0));
     74       return;
     75     }
     76 
     77     xla::ComputationBuilder* builder = ctx->builder();
     78 
     79     std::vector<int64> window_strides(input_shape.dims(), 1);
     80     std::vector<int64> window_dims(input_shape.dims(), 1);
     81     window_dims[axis] = input_shape.dim_size(axis);
     82 
     83     std::vector<std::pair<int64, int64>> padding(input_shape.dims(), {0, 0});
     84     padding[axis].first = input_shape.dim_size(axis) - 1;
     85     // In exclusive mode, add an extra padding element so there is a complete
     86     // window of padding before the data starts.
     87     if (exclusive_) {
     88       ++padding[axis].first;
     89     }
     90     if (reverse_) {
     91       std::swap(padding[axis].first, padding[axis].second);
     92     }
     93 
     94     xla::ComputationDataHandle input = ctx->Input(0);
     95     xla::ComputationDataHandle init;
     96     const xla::Computation* reducer;
     97     if (sum_) {
     98       init = XlaHelpers::Zero(builder, dtype);
     99       reducer = ctx->GetOrCreateAdd(dtype);
    100     } else {
    101       init = XlaHelpers::One(builder, dtype);
    102       reducer = ctx->GetOrCreateMul(dtype);
    103     }
    104     auto output = builder->ReduceWindowWithGeneralPadding(
    105         ctx->Input(0), init, *reducer, window_dims, window_strides, padding);
    106 
    107     // In exclusive mode, we have computed an extra element containing the sum
    108     // of all the input elements. Slice off this extra "last" element.
    109     if (exclusive_) {
    110       if (reverse_) {
    111         output = builder->SliceInDim(output, 1, input_shape.dim_size(axis) + 1,
    112                                      1, axis);
    113 
    114       } else {
    115         output =
    116             builder->SliceInDim(output, 0, input_shape.dim_size(axis), 1, axis);
    117       }
    118     }
    119     ctx->SetOutput(0, output);
    120   }
    121 
    122  private:
    123   const bool sum_;  // True=cumulative sum. False=cumulative product.
    124   bool reverse_;
    125   bool exclusive_;
    126 };
    127 
    128 class CumsumOp : public ScanOp {
    129  public:
    130   explicit CumsumOp(OpKernelConstruction* ctx) : ScanOp(ctx, /*sum=*/true) {}
    131 };
    132 REGISTER_XLA_OP(Name("Cumsum")
    133                     .TypeConstraint("T", kScanOpTypes)
    134                     .CompileTimeConstInput("axis"),
    135                 CumsumOp);
    136 
    137 class CumprodOp : public ScanOp {
    138  public:
    139   explicit CumprodOp(OpKernelConstruction* ctx) : ScanOp(ctx, /*sum=*/false) {}
    140 };
    141 REGISTER_XLA_OP(Name("Cumprod")
    142                     .TypeConstraint("T", kScanOpTypes)
    143                     .CompileTimeConstInput("axis"),
    144                 CumprodOp);
    145 
    146 }  // anonymous namespace
    147 }  // namespace tensorflow
    148