Home | History | Annotate | Download | only in service
      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 "tensorflow/compiler/xla/service/shape_inference.h"
     17 
     18 #include <stddef.h>
     19 #include <algorithm>
     20 #include <numeric>
     21 #include <set>
     22 #include <string>
     23 
     24 #include "tensorflow/compiler/xla/shape_util.h"
     25 #include "tensorflow/compiler/xla/status_macros.h"
     26 #include "tensorflow/compiler/xla/types.h"
     27 #include "tensorflow/compiler/xla/util.h"
     28 #include "tensorflow/compiler/xla/window_util.h"
     29 #include "tensorflow/compiler/xla/xla_data.pb.h"
     30 #include "tensorflow/core/lib/core/errors.h"
     31 #include "tensorflow/core/lib/core/stringpiece.h"
     32 #include "tensorflow/core/lib/gtl/flatset.h"
     33 #include "tensorflow/core/lib/math/math_util.h"
     34 #include "tensorflow/core/lib/strings/str_util.h"
     35 #include "tensorflow/core/lib/strings/strcat.h"
     36 #include "tensorflow/core/lib/strings/stringprintf.h"
     37 #include "tensorflow/core/platform/logging.h"
     38 #include "tensorflow/core/platform/protobuf.h"
     39 
     40 using tensorflow::str_util::Join;
     41 using tensorflow::strings::Printf;
     42 
     43 namespace xla {
     44 
     45 namespace {
     46 
     47 // Return the UnaryOperation proto enum value associated with the given HLO
     48 // opcode.
     49 UnaryOperation OpcodeToUnaryOperation(HloOpcode opcode) {
     50   switch (opcode) {
     51     case HloOpcode::kAbs:
     52       return UNOP_ABS;
     53     case HloOpcode::kCeil:
     54       return UNOP_CEIL;
     55     case HloOpcode::kCos:
     56       return UNOP_COS;
     57     case HloOpcode::kExp:
     58       return UNOP_EXP;
     59     case HloOpcode::kFloor:
     60       return UNOP_FLOOR;
     61     case HloOpcode::kImag:
     62       return UNOP_IMAG;
     63     case HloOpcode::kIsFinite:
     64       return UNOP_IS_FINITE;
     65     case HloOpcode::kLog:
     66       return UNOP_LOG;
     67     case HloOpcode::kNot:
     68       return UNOP_NOT;
     69     case HloOpcode::kNegate:
     70       return UNOP_NEGATE;
     71     case HloOpcode::kReal:
     72       return UNOP_REAL;
     73     case HloOpcode::kRoundNearestAfz:
     74       return UNOP_ROUND_NEAREST_AFZ;
     75     case HloOpcode::kSign:
     76       return UNOP_SIGN;
     77     case HloOpcode::kSin:
     78       return UNOP_SIN;
     79     case HloOpcode::kSort:
     80       return UNOP_SORT;
     81     case HloOpcode::kTanh:
     82       return UNOP_TANH;
     83     default:
     84       LOG(FATAL) << "Unhandled opcode for conversion to unary operation: "
     85                  << opcode;
     86   }
     87 }
     88 
     89 // Return the BinaryOperation proto enum value associated with the given HLO
     90 // opcode.
     91 BinaryOperation OpcodeToBinaryOperation(HloOpcode opcode) {
     92   switch (opcode) {
     93     case HloOpcode::kAtan2:
     94       return BINOP_ATAN2;
     95     case HloOpcode::kComplex:
     96       return BINOP_COMPLEX;
     97     case HloOpcode::kMultiply:
     98       return BINOP_MUL;
     99     case HloOpcode::kAdd:
    100       return BINOP_ADD;
    101     case HloOpcode::kSubtract:
    102       return BINOP_SUB;
    103     case HloOpcode::kDivide:
    104       return BINOP_DIV;
    105     case HloOpcode::kEq:
    106       return BINOP_EQ;
    107     case HloOpcode::kGe:
    108       return BINOP_GE;
    109     case HloOpcode::kGt:
    110       return BINOP_GT;
    111     case HloOpcode::kLe:
    112       return BINOP_LE;
    113     case HloOpcode::kLt:
    114       return BINOP_LT;
    115     case HloOpcode::kNe:
    116       return BINOP_NE;
    117     case HloOpcode::kMaximum:
    118       return BINOP_MAX;
    119     case HloOpcode::kMinimum:
    120       return BINOP_MIN;
    121     case HloOpcode::kPower:
    122       return BINOP_POW;
    123     case HloOpcode::kRemainder:
    124       return BINOP_REM;
    125     case HloOpcode::kOr:
    126       return BINOP_OR;
    127     case HloOpcode::kAnd:
    128       return BINOP_AND;
    129     case HloOpcode::kShiftLeft:
    130       return BINOP_SHIFT_LEFT;
    131     case HloOpcode::kShiftRightArithmetic:
    132       return BINOP_SHIFT_RIGHT_ARITHMETIC;
    133     case HloOpcode::kShiftRightLogical:
    134       return BINOP_SHIFT_RIGHT_LOGICAL;
    135     default:
    136       LOG(FATAL) << "unhandled opcode " << opcode;
    137   }
    138 }
    139 
    140 // Return the TernaryOperation proto enum value associated with the given HLO
    141 // opcode.
    142 TernaryOperation OpcodeToTernaryOperation(HloOpcode opcode) {
    143   switch (opcode) {
    144     case HloOpcode::kClamp:
    145       return TRIOP_CLAMP;
    146     case HloOpcode::kSelect:
    147       return TRIOP_SELECT;
    148     default:
    149       LOG(FATAL) << "unhandled opcode " << opcode;
    150   }
    151 }
    152 
    153 // Return the VariadicOperation proto enum value associated with the given HLO
    154 // opcode.
    155 VariadicOperation OpcodeToVariadicOperation(HloOpcode opcode) {
    156   switch (opcode) {
    157     case HloOpcode::kTuple:
    158       return VAROP_TUPLE;
    159     default:
    160       LOG(FATAL) << "unhandled opcode " << opcode;
    161   }
    162 }
    163 
    164 // Returns true if no element is present in slice more than once.
    165 bool AllUnique(tensorflow::gtl::ArraySlice<int64> slice) {
    166   return std::set<int64>(slice.begin(), slice.end()).size() == slice.size();
    167 }
    168 
    169 tensorflow::Status ExpectNotTupleOrOpaque(const Shape& shape,
    170                                           tensorflow::StringPiece op_type) {
    171   if (ShapeUtil::IsTuple(shape)) {
    172     return InvalidArgument("Expected non-tuple argument for %s. Got: %s",
    173                            op_type.ToString().c_str(),
    174                            ShapeUtil::HumanString(shape).c_str());
    175   } else if (ShapeUtil::IsOpaque(shape)) {
    176     return InvalidArgument("Expected non-opaque argument for %s. Got: %s",
    177                            op_type.ToString().c_str(),
    178                            ShapeUtil::HumanString(shape).c_str());
    179   } else {
    180     return tensorflow::Status::OK();
    181   }
    182 }
    183 
    184 tensorflow::Status VerifyReducerShape(const ProgramShape& reducer_shape,
    185                                       const Shape& init_value_shape,
    186                                       const PrimitiveType& input_element_type) {
    187   if (reducer_shape.parameters_size() != 2) {
    188     return InvalidArgument(
    189         "Reduction function must take 2 parameters, but "
    190         "takes %d parameter(s).",
    191         reducer_shape.parameters_size());
    192   }
    193 
    194   const Shape& accumulator_shape = reducer_shape.result();
    195   if (ShapeUtil::Rank(accumulator_shape) != 0) {
    196     return Unimplemented(
    197         "Reduction function currently must have rank-0 result.");
    198   }
    199 
    200   // Check that the accumulator can be passed in as the first argument.
    201   // Note: comparing here and below with Compatible since we don't care about
    202   // layout in scalars - see b/26668201 for a longer-term vision.
    203   if (!ShapeUtil::Compatible(accumulator_shape, reducer_shape.parameters(0))) {
    204     return InvalidArgument(
    205         "Reduction function's first parameter shape differs from the "
    206         "result shape: %s vs %s",
    207         ShapeUtil::HumanString(reducer_shape.parameters(0)).c_str(),
    208         ShapeUtil::HumanString(accumulator_shape).c_str());
    209   }
    210 
    211   // Check that init_value's shape is suitable for reducer_shape.
    212   if (!ShapeUtil::CompatibleIgnoringFpPrecision(accumulator_shape,
    213                                                 init_value_shape)) {
    214     return InvalidArgument(
    215         "Reduction function's accumulator shape differs from the "
    216         "init_value shape: %s vs %s",
    217         ShapeUtil::HumanString(accumulator_shape).c_str(),
    218         ShapeUtil::HumanString(init_value_shape).c_str());
    219   }
    220 
    221   // Check that the inputs can be passed in as the second argument.
    222   const Shape& input_element_shape =
    223       ShapeUtil::MakeShape(input_element_type, {});
    224   if (!ShapeUtil::CompatibleIgnoringFpPrecision(input_element_shape,
    225                                                 reducer_shape.parameters(1))) {
    226     return InvalidArgument(
    227         "Reduction function's second parameter shape differs from the "
    228         "input type element type: %s vs %s",
    229         ShapeUtil::HumanString(reducer_shape.parameters(1)).c_str(),
    230         ShapeUtil::HumanString(input_element_shape).c_str());
    231   }
    232 
    233   // Currently the accumulator and inputs must be the same type,
    234   // though that restriction could be relaxed.
    235   if (!ShapeUtil::CompatibleIgnoringFpPrecision(accumulator_shape,
    236                                                 reducer_shape.parameters(1))) {
    237     return InvalidArgument(
    238         "Reduction function's second parameter shape currently must "
    239         "match the result shape. Got %s vs %s",
    240         ShapeUtil::HumanString(reducer_shape.parameters(1)).c_str(),
    241         ShapeUtil::HumanString(accumulator_shape).c_str());
    242   }
    243 
    244   return tensorflow::Status::OK();
    245 }
    246 
    247 StatusOr<Shape> InferWindowOutputShape(const Shape& base_shape,
    248                                        const Window& window,
    249                                        PrimitiveType element_type,
    250                                        bool allow_negative_padding) {
    251   if (window.dimensions_size() != ShapeUtil::Rank(base_shape)) {
    252     return InvalidArgument(
    253         "Window has dimension %d but base shape has dimension %lld.",
    254         window.dimensions_size(), ShapeUtil::Rank(base_shape));
    255   }
    256 
    257   std::vector<int64> output_dimensions(window.dimensions_size());
    258   for (int64 i = 0; i < window.dimensions_size(); ++i) {
    259     const auto& dim = window.dimensions(i);
    260     if (dim.size() <= 0) {
    261       return InvalidArgument("Window has a non-positive dimension. Window: %s",
    262                              window.DebugString().c_str());
    263     }
    264     if (dim.stride() <= 0) {
    265       return InvalidArgument("Window has a non-positive stride. Window: %s",
    266                              window.DebugString().c_str());
    267     }
    268     if (!allow_negative_padding && dim.padding_low() < 0) {
    269       return InvalidArgument("Window has a negative low padding. Window: %s",
    270                              window.DebugString().c_str());
    271     }
    272     if (!allow_negative_padding && dim.padding_high() < 0) {
    273       return InvalidArgument("Window has a negative high padding. Window: %s",
    274                              window.DebugString().c_str());
    275     }
    276     if (dim.base_dilation() < 1) {
    277       return InvalidArgument(
    278           "Window has a non-positive base area dilation factor. Window: %s",
    279           window.DebugString().c_str());
    280     }
    281     if (dim.window_dilation() < 1) {
    282       return InvalidArgument(
    283           "Window has a non-positive window dilation factor. Window: %s",
    284           window.DebugString().c_str());
    285     }
    286 
    287     const int64 dilated_base = window_util::DilatedBound(
    288         ShapeUtil::GetDimension(base_shape, i), dim.base_dilation());
    289     const int64 padded_dilated_base =
    290         dim.padding_low() + dilated_base + dim.padding_high();
    291     const int64 dilated_window =
    292         window_util::DilatedBound(dim.size(), dim.window_dilation());
    293 
    294     output_dimensions[i] = window_util::StridedBound(
    295         padded_dilated_base, dilated_window, dim.stride());
    296   }
    297 
    298   return ShapeUtil::MakeShape(element_type, output_dimensions);
    299 }
    300 
    301 }  // namespace
    302 
    303 /* static */ StatusOr<Shape> ShapeInference::InferUnaryOpShape(
    304     HloOpcode opcode, const HloInstruction* operand) {
    305   // There is no copy operation at the proto level, so handle copy explicitly.
    306   if (opcode == HloOpcode::kCopy) {
    307     return operand->shape();
    308   }
    309 
    310   return InferUnaryOpShape(OpcodeToUnaryOperation(opcode), operand->shape());
    311 }
    312 
    313 /* static */ StatusOr<Shape> ShapeInference::InferUnaryOpShape(
    314     UnaryOperation operation, const Shape& arg) {
    315   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(arg, "operand of unary operation"));
    316 
    317   TF_DCHECK_OK(ShapeUtil::ValidateShapeWithOptionalLayout(arg));
    318   switch (operation) {
    319     case UNOP_FLOOR:
    320     case UNOP_CEIL:
    321       if (!ShapeUtil::ElementIsFloating(arg)) {
    322         return InvalidArgument(
    323             "expected element type in shape to be floating for floor/ceil "
    324             "operation; got %s",
    325             PrimitiveType_Name(arg.element_type()).c_str());
    326       }
    327       return arg;
    328     case UNOP_COS:
    329     case UNOP_SIN:
    330     case UNOP_EXP:
    331     case UNOP_LOG:
    332     case UNOP_TANH:
    333       if (!ShapeUtil::ElementIsFloating(arg) &&
    334           !ShapeUtil::ElementIsComplex(arg)) {
    335         return InvalidArgument(
    336             "expected element type in shape to be floating or complex for "
    337             "sin/cos/exp/log/tanh operation; got %s",
    338             PrimitiveType_Name(arg.element_type()).c_str());
    339       }
    340       return arg;
    341     case UNOP_REAL:
    342     case UNOP_IMAG:
    343       if (!ShapeUtil::ElementIsComplex(arg)) {
    344         return InvalidArgument(
    345             "expected element type in shape to be complex for real/imag "
    346             "operation; got %s",
    347             PrimitiveType_Name(arg.element_type()).c_str());
    348       }
    349       return ShapeUtil::ChangeElementType(arg, F32);
    350     case UNOP_ABS:
    351       if (ShapeUtil::ElementIsComplex(arg)) {
    352         return ShapeUtil::ChangeElementType(
    353             arg, primitive_util::ComplexComponentType(arg.element_type()));
    354       }
    355       return arg;
    356     case UNOP_NEGATE:
    357     case UNOP_ROUND_NEAREST_AFZ:
    358     case UNOP_SIGN:
    359     case UNOP_SORT:
    360       return arg;
    361 
    362     case UNOP_NOT:
    363       if (arg.element_type() != PRED &&
    364           !primitive_util::IsIntegralType(arg.element_type())) {
    365         return InvalidArgument(
    366             "expected pred or an integral element type in argument to not "
    367             "operation; got %s",
    368             PrimitiveType_Name(arg.element_type()).c_str());
    369       }
    370       return arg;
    371 
    372     case UNOP_IS_FINITE:
    373       if (!ShapeUtil::ElementIsFloating(arg)) {
    374         return InvalidArgument(
    375             "expected element type in shape to be floating point for IsFinite "
    376             "operation; got %s",
    377             PrimitiveType_Name(arg.element_type()).c_str());
    378       }
    379       return ShapeUtil::ChangeElementType(arg, PRED);
    380 
    381     default:
    382       return InvalidArgument(
    383           "Unknown operation for unary shape inference: \"%s\".",
    384           UnaryOperation_Name(operation).c_str());
    385   }
    386 }
    387 
    388 /* static */ StatusOr<Shape> ShapeInference::InferConcatOpShape(
    389     tensorflow::gtl::ArraySlice<const Shape*> arg_shapes,
    390     const int64 dimension) {
    391   if (arg_shapes.empty()) {
    392     return InvalidArgument("Concatenate expects at least one argument");
    393   }
    394   if (dimension < 0 || dimension >= ShapeUtil::Rank(*arg_shapes[0])) {
    395     return InvalidArgument("dimension to concatenate along out of bounds: %lld",
    396                            dimension);
    397   }
    398   const Shape* arg_shape = nullptr;
    399   PrimitiveType element_type = PRIMITIVE_TYPE_INVALID;
    400   for (const Shape* shape : arg_shapes) {
    401     TF_RETURN_IF_ERROR(
    402         ExpectNotTupleOrOpaque(*shape, "operand of concatenation"));
    403     if (!arg_shape) {
    404       arg_shape = shape;
    405       element_type = arg_shape->element_type();
    406       continue;
    407     }
    408     if (ShapeUtil::Rank(*arg_shape) != ShapeUtil::Rank(*shape)) {
    409       return InvalidArgument(
    410           "Cannot concatenate arrays with different ranks: %lld (%s) vs %lld "
    411           "(%s)",
    412           ShapeUtil::Rank(*arg_shape),
    413           ShapeUtil::HumanString(*arg_shape).c_str(), ShapeUtil::Rank(*shape),
    414           ShapeUtil::HumanString(*shape).c_str());
    415     }
    416     if (!ShapeUtil::SameElementTypeIgnoringFpPrecision(*arg_shape, *shape)) {
    417       return InvalidArgument(
    418           "cannot concatenate arrays with different element types: %s vs %s",
    419           PrimitiveType_Name(arg_shape->element_type()).c_str(),
    420           PrimitiveType_Name(shape->element_type()).c_str());
    421     }
    422     for (int64 dimension_number = 0;
    423          dimension_number < ShapeUtil::Rank(*arg_shape); ++dimension_number) {
    424       if (arg_shape->dimensions(dimension_number) !=
    425           shape->dimensions(dimension_number)) {
    426         if (dimension_number == dimension) {
    427           continue;  // It's okay to differ in the dimension we're
    428                      // concatenating.
    429         }
    430         return InvalidArgument(
    431             "cannot concatenate arrays that differ in dimensions other than "
    432             "the one being concatenated (the other array dimensions must be "
    433             "the same): %s vs %s in dimension %lld",
    434             ShapeUtil::HumanString(*arg_shape).c_str(),
    435             ShapeUtil::HumanString(*shape).c_str(), dimension);
    436       }
    437     }
    438     element_type = ShapeUtil::HigherPrecisionElementType(*shape, *arg_shape);
    439   }
    440 
    441   std::vector<int64> new_dimensions(arg_shape->dimensions().begin(),
    442                                     arg_shape->dimensions().end());
    443   for (size_t i = 1; i < arg_shapes.size(); ++i) {
    444     new_dimensions[dimension] += arg_shapes[i]->dimensions(dimension);
    445   }
    446   return ShapeUtil::MakeShape(element_type, new_dimensions);
    447 }
    448 
    449 /* static */ StatusOr<Shape> ShapeInference::InferConvertShape(
    450     const Shape& operand_shape, PrimitiveType new_element_type) {
    451   auto old_element_type = operand_shape.element_type();
    452   if (primitive_util::IsComplexType(old_element_type) &&
    453       !primitive_util::IsComplexType(new_element_type)) {
    454     return Unimplemented(
    455         "Unsupported conversion from complex to real type: %s => %s",
    456         ShapeUtil::HumanString(operand_shape).c_str(),
    457         PrimitiveType_Name(new_element_type).c_str());
    458   }
    459   if (ShapeUtil::IsTuple(operand_shape) || new_element_type == TUPLE) {
    460     // Note: we may want to support tuple conversions via this operation in the
    461     // future, by recursing into the tuple elements to check all sub-conversions
    462     // are valid. For now we just reject them, though.
    463     return InvalidArgument(
    464         "cannot convert from or to tuple type; requested conversion: %s => %s",
    465         ShapeUtil::HumanString(operand_shape).c_str(),
    466         PrimitiveType_Name(new_element_type).c_str());
    467   }
    468 
    469   return ShapeUtil::ChangeElementType(operand_shape, new_element_type);
    470 }
    471 
    472 /* static */ StatusOr<Shape> ShapeInference::InferBitcastConvertShape(
    473     const Shape& operand_shape, PrimitiveType new_element_type) {
    474   auto old_element_type = operand_shape.element_type();
    475   if (primitive_util::IsComplexType(old_element_type) !=
    476       primitive_util::IsComplexType(new_element_type)) {
    477     return Unimplemented(
    478         "Unsupported conversion between real and complex types: %s => %s",
    479         ShapeUtil::HumanString(operand_shape).c_str(),
    480         PrimitiveType_Name(new_element_type).c_str());
    481   }
    482   if (ShapeUtil::IsTuple(operand_shape) || new_element_type == TUPLE) {
    483     // Note: we may want to support tuple conversions via this operation in the
    484     // future, by recursing into the tuple elements to check all sub-conversions
    485     // are valid. For now we just reject them, though.
    486     return InvalidArgument(
    487         "cannot convert from or to tuple type; requested conversion: %s => %s",
    488         ShapeUtil::HumanString(operand_shape).c_str(),
    489         PrimitiveType_Name(new_element_type).c_str());
    490   }
    491   if (primitive_util::BitWidth(old_element_type) !=
    492       primitive_util::BitWidth(new_element_type)) {
    493     return InvalidArgument(
    494         "cannot bitcast types with different bit-widths: %s => %s",
    495         PrimitiveType_Name(old_element_type).c_str(),
    496         PrimitiveType_Name(new_element_type).c_str());
    497   }
    498 
    499   return ShapeUtil::ChangeElementType(operand_shape, new_element_type);
    500 }
    501 
    502 /* static */ StatusOr<Shape> ShapeInference::InferReducePrecisionShape(
    503     const Shape& operand_shape, const int exponent_bits,
    504     const int mantissa_bits) {
    505   if (!ShapeUtil::ElementIsFloating(operand_shape)) {
    506     return InvalidArgument(
    507         "expected element type in shape to be floating point for "
    508         "ReducePrecision operation; got %s",
    509         PrimitiveType_Name(operand_shape.element_type()).c_str());
    510   }
    511   if (exponent_bits < 1) {
    512     // One exponent bit is necessary to distinguish 0 from infinity.  Having
    513     // no exponent bits doesn't produce a sensible number, so we require at
    514     // least one.
    515     return InvalidArgument("expected exponent_bits >= 1; got %d",
    516                            exponent_bits);
    517   }
    518   if (mantissa_bits < 0) {
    519     // A number with no mantissa bits is still meaningful, however.
    520     return InvalidArgument("expected non-negative mantissa_bits; got %d",
    521                            mantissa_bits);
    522   }
    523   return operand_shape;
    524 }
    525 
    526 /* static */ StatusOr<Shape> ShapeInference::InferPadShape(
    527     const Shape& operand_shape, const Shape& padding_value_shape,
    528     const PaddingConfig& padding_config) {
    529   if (ShapeUtil::IsTuple(operand_shape)) {
    530     return InvalidArgument(
    531         "pad operation does not support tuple-shape operands");
    532   }
    533   if (!ShapeUtil::IsScalar(padding_value_shape)) {
    534     return InvalidArgument(
    535         "pad operation does not support non-scalar padding values");
    536   }
    537   if (ShapeUtil::Rank(operand_shape) != padding_config.dimensions_size()) {
    538     return InvalidArgument(
    539         "The rank of the operand and the padding configuration do not match: "
    540         "%s vs %s",
    541         ShapeUtil::HumanString(operand_shape).c_str(),
    542         padding_config.ShortDebugString().c_str());
    543   }
    544   if (!ShapeUtil::SameElementTypeIgnoringFpPrecision(operand_shape,
    545                                                      padding_value_shape)) {
    546     return InvalidArgument(
    547         "the element types of the operands to pad do not match");
    548   }
    549   std::vector<int64> dimensions(ShapeUtil::Rank(operand_shape));
    550   for (int64 i = 0; i < operand_shape.dimensions_size(); ++i) {
    551     dimensions[i] = operand_shape.dimensions(i) +
    552                     padding_config.dimensions(i).edge_padding_low() +
    553                     padding_config.dimensions(i).edge_padding_high() +
    554                     std::max<int64>(operand_shape.dimensions(i) - 1, 0LL) *
    555                         padding_config.dimensions(i).interior_padding();
    556   }
    557   return ShapeUtil::MakeShape(
    558       ShapeUtil::HigherPrecisionElementType(operand_shape, padding_value_shape),
    559       dimensions);
    560 }
    561 
    562 // Current DotDimensionNumbers Requirements:
    563 //
    564 // Contracting Dimensions:
    565 // *) Exactly one contracting dimension on both lhs and rhs.
    566 // *) Contracting dimension size must be the same on both lhs and rhs.
    567 // *) Contracting dimension numbers do not need to be the same (i.e. transposes
    568 //    are passed on to emitter implementations).
    569 //
    570 // Batch Dimensions:
    571 // *) Same number of batch dimensions on both lhs and rhs.
    572 // *) Same batch dimension numbers (and sizes) on both lhs and rhs.
    573 // *) Batch dimension numbers must be ordered before contracting and
    574 //    non-contracting/non-batch dimension numbers.
    575 //
    576 // Non-Contracting-Non-Batch Dimensions:
    577 // *) Can be 0 (matrix-vector) or 1 (matrix-matrix).
    578 //
    579 
    580 namespace {
    581 
    582 Status ValidateDotDimensionNumbers(
    583     const Shape& lhs, const Shape& rhs,
    584     const DotDimensionNumbers& dimension_numbers) {
    585   // Check that dimension numbers are in range.
    586   auto dims_in_range =
    587       [](const int64 rank, tensorflow::gtl::ArraySlice<int64> contracting_dims,
    588          tensorflow::gtl::ArraySlice<int64> batch_dims) -> bool {
    589     auto in_range = [&rank](int64 i) -> bool { return 0 <= i && i < rank; };
    590     return std::all_of(contracting_dims.begin(), contracting_dims.end(),
    591                        in_range) &&
    592            std::all_of(batch_dims.begin(), batch_dims.end(), in_range);
    593   };
    594 
    595   tensorflow::gtl::ArraySlice<int64> lhs_contracting_dimensions =
    596       AsInt64Slice(dimension_numbers.lhs_contracting_dimensions());
    597   tensorflow::gtl::ArraySlice<int64> rhs_contracting_dimensions =
    598       AsInt64Slice(dimension_numbers.rhs_contracting_dimensions());
    599   tensorflow::gtl::ArraySlice<int64> lhs_batch_dimensions =
    600       AsInt64Slice(dimension_numbers.lhs_batch_dimensions());
    601   tensorflow::gtl::ArraySlice<int64> rhs_batch_dimensions =
    602       AsInt64Slice(dimension_numbers.rhs_batch_dimensions());
    603 
    604   if (!dims_in_range(ShapeUtil::Rank(lhs), lhs_contracting_dimensions,
    605                      lhs_batch_dimensions) ||
    606       !dims_in_range(ShapeUtil::Rank(rhs), rhs_contracting_dimensions,
    607                      rhs_batch_dimensions)) {
    608     return InvalidArgument("A dimension number is out of range in dot: %s",
    609                            dimension_numbers.DebugString().c_str());
    610   }
    611 
    612   // Check that dimension numbers are unique.
    613   auto dims_unique = [](tensorflow::gtl::ArraySlice<int64> contracting_dims,
    614                         tensorflow::gtl::ArraySlice<int64> batch_dims) -> bool {
    615     tensorflow::gtl::FlatSet<int64> dim_set;
    616     auto is_unique = [&dim_set](int64 i) -> bool {
    617       return dim_set.insert(i).second;
    618     };
    619     return std::all_of(contracting_dims.begin(), contracting_dims.end(),
    620                        is_unique) &&
    621            std::all_of(batch_dims.begin(), batch_dims.end(), is_unique);
    622   };
    623 
    624   if (!dims_unique(lhs_contracting_dimensions, lhs_batch_dimensions) ||
    625       !dims_unique(rhs_contracting_dimensions, rhs_batch_dimensions)) {
    626     return InvalidArgument("A dimension number is not unique in dot: %s",
    627                            dimension_numbers.DebugString().c_str());
    628   }
    629 
    630   // Check that the count of non-contracting-non-batch dimensions is in {0, 1}.
    631   const int64 lhs_non_contracting_non_batch_dims =
    632       ShapeUtil::Rank(lhs) -
    633       dimension_numbers.lhs_contracting_dimensions_size() -
    634       dimension_numbers.lhs_batch_dimensions_size();
    635   const int64 rhs_non_contracting_non_batch_dims =
    636       ShapeUtil::Rank(rhs) -
    637       dimension_numbers.rhs_contracting_dimensions_size() -
    638       dimension_numbers.rhs_batch_dimensions_size();
    639   if (lhs_non_contracting_non_batch_dims < 0 ||
    640       lhs_non_contracting_non_batch_dims > 1 ||
    641       rhs_non_contracting_non_batch_dims < 0 ||
    642       rhs_non_contracting_non_batch_dims > 1) {
    643     return InvalidArgument(
    644         "batch and contracting dimension number mismatch "
    645         "with rank ");
    646   }
    647 
    648   // Check that batch dimension numbers are ordered before all others, and
    649   // that they are monotonically increasing.
    650   std::vector<int64> batch_dim_numbers(lhs_batch_dimensions.size());
    651   std::iota(batch_dim_numbers.begin(), batch_dim_numbers.end(), 0);
    652   if (!std::equal(batch_dim_numbers.begin(), batch_dim_numbers.end(),
    653                   lhs_batch_dimensions.begin()) ||
    654       !std::equal(batch_dim_numbers.begin(), batch_dim_numbers.end(),
    655                   rhs_batch_dimensions.begin())) {
    656     return InvalidArgument(
    657         "batch dimension numbers must precede non-batch dimensions and be"
    658         "monotonically increasing.");
    659   }
    660 
    661   return Status::OK();
    662 }
    663 
    664 }  // namespace
    665 
    666 /* static */ StatusOr<Shape> ShapeInference::InferDotOpShape(
    667     const Shape& lhs, const Shape& rhs,
    668     const DotDimensionNumbers& dimension_numbers) {
    669   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(lhs, "lhs of dot"));
    670   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(rhs, "rhs of dot"));
    671 
    672   auto fail = [lhs, rhs](const string& addendum) -> Status {
    673     string message = tensorflow::strings::Printf(
    674         "cannot infer shape for dot operation: %s <dot> %s",
    675         ShapeUtil::HumanString(lhs).c_str(),
    676         ShapeUtil::HumanString(rhs).c_str());
    677     if (!addendum.empty()) {
    678       message += ": " + addendum;
    679     }
    680     return InvalidArgument("%s", message.c_str());
    681   };
    682 
    683   // Check if both element types are the same.
    684   if (!ShapeUtil::SameElementTypeIgnoringFpPrecision(lhs, rhs)) {
    685     return fail("element types do not match");
    686   }
    687 
    688   if ((ShapeUtil::Rank(lhs) < 1) || (ShapeUtil::Rank(rhs) < 1)) {
    689     return fail("dot only supports rank 1 or above.");
    690   }
    691 
    692   // Validate basic properties of dot dimension numbers.
    693   TF_RETURN_IF_ERROR(ValidateDotDimensionNumbers(lhs, rhs, dimension_numbers));
    694 
    695   // Check that there is only one contracting dimension for both lhs and rhs.
    696   if (dimension_numbers.lhs_contracting_dimensions_size() !=
    697           dimension_numbers.rhs_contracting_dimensions_size() ||
    698       dimension_numbers.lhs_contracting_dimensions_size() != 1) {
    699     return fail("must specify one contracting dimension for both lhs and rhs.");
    700   }
    701 
    702   // Check that contracting dimension sizes match.
    703   const int64 lhs_contracting_dimension =
    704       dimension_numbers.lhs_contracting_dimensions(0);
    705   const int64 rhs_contracting_dimension =
    706       dimension_numbers.rhs_contracting_dimensions(0);
    707   if (lhs.dimensions(lhs_contracting_dimension) !=
    708       rhs.dimensions(rhs_contracting_dimension)) {
    709     return fail("contracting dimension sizes do not match.");
    710   }
    711 
    712   // Check that number of batch dimensions match.
    713   if (dimension_numbers.lhs_batch_dimensions_size() !=
    714       dimension_numbers.rhs_batch_dimensions_size()) {
    715     return fail("must the same number of batch dimensions for lhs and rhs.");
    716   }
    717 
    718   // Check that batch dimension numbers and sizes match.
    719   for (int64 i = 0; i < dimension_numbers.lhs_batch_dimensions_size(); ++i) {
    720     if (dimension_numbers.lhs_batch_dimensions(i) !=
    721             dimension_numbers.rhs_batch_dimensions(i) ||
    722         lhs.dimensions(dimension_numbers.lhs_batch_dimensions(i)) !=
    723             rhs.dimensions(dimension_numbers.rhs_batch_dimensions(i))) {
    724       return fail("batch dimension numbers and sizes must match for lhs/rhs.");
    725     }
    726   }
    727 
    728   // The ranks of lhs and rhs are decremented by 1 respectively due to the
    729   // contraction, and added for the rank of the result. When an input tensor is
    730   // a scalar, its contribution to the rank of the result is 0.
    731   // Generate the result dimensions in order, rhs dimensions followed by lhs
    732   // dimensions except the contracted and batch dimensions.
    733   std::vector<int64> dimensions;
    734   std::unordered_set<int64> rhs_batch_dims(
    735       dimension_numbers.rhs_batch_dimensions().begin(),
    736       dimension_numbers.rhs_batch_dimensions().end());
    737   for (int64 i = 0; i < ShapeUtil::Rank(lhs); i++) {
    738     if (i != lhs_contracting_dimension) {
    739       dimensions.push_back(lhs.dimensions(i));
    740     }
    741   }
    742   for (int64 i = 0; i < ShapeUtil::Rank(rhs); i++) {
    743     if (i != rhs_contracting_dimension && rhs_batch_dims.count(i) == 0) {
    744       dimensions.push_back(rhs.dimensions(i));
    745     }
    746   }
    747   Shape result = ShapeUtil::MakeShape(
    748       ShapeUtil::HigherPrecisionElementType(lhs, rhs), dimensions);
    749 
    750   TF_DCHECK_OK(ShapeUtil::ValidateShapeWithOptionalLayout(result));
    751   VLOG(2) << "inferred dot shape: " << ShapeUtil::HumanString(result);
    752   return result;
    753 }
    754 
    755 /* static */ StatusOr<Shape>
    756 ShapeInference::InferDegenerateDimensionBroadcastShape(
    757     BinaryOperation operation, const Shape& lhs, const Shape& rhs) {
    758   TF_RET_CHECK(ShapeUtil::Rank(lhs) == ShapeUtil::Rank(rhs));
    759 
    760   // The shapes have to be compatible. That is, if some dimension d has a
    761   // different size in the two shapes, one of them has to be 1 (a "degenerate"
    762   // dimension). In that case, the output shape has the non-1 dimension size
    763   // from the lhs/rhs pair in every index.
    764   std::vector<int64> output_dimensions(ShapeUtil::Rank(lhs));
    765   for (int64 i = 0; i < ShapeUtil::Rank(lhs); ++i) {
    766     if (lhs.dimensions(i) == rhs.dimensions(i)) {
    767       output_dimensions[i] = lhs.dimensions(i);
    768     } else if (lhs.dimensions(i) == 1) {
    769       output_dimensions[i] = rhs.dimensions(i);
    770     } else if (rhs.dimensions(i) == 1) {
    771       output_dimensions[i] = lhs.dimensions(i);
    772     } else {
    773       return InvalidArgument("binary op %s with incompatible shapes: %s and %s",
    774                              BinaryOperation_Name(operation).c_str(),
    775                              ShapeUtil::HumanString(lhs).c_str(),
    776                              ShapeUtil::HumanString(rhs).c_str());
    777     }
    778   }
    779   return ShapeUtil::MakeShape(ShapeUtil::HigherPrecisionElementType(lhs, rhs),
    780                               output_dimensions);
    781 }
    782 
    783 /* static */ StatusOr<Shape> ShapeInference::InferInDimBroadcastShape(
    784     BinaryOperation operation, const Shape& smaller_shape,
    785     const Shape& larger_shape,
    786     tensorflow::gtl::ArraySlice<int64> broadcast_dimensions) {
    787   if (broadcast_dimensions.empty() && !ShapeUtil::IsScalar(smaller_shape)) {
    788     // Reject "magic" inference for binops on different shapes, requiring
    789     // the user to provide an explicit broadcast dimension in this case.
    790     // See b/25177275 for more details.
    791     return InvalidArgument("automatic shape inference not supported: %s and %s",
    792                            ShapeUtil::HumanString(smaller_shape).c_str(),
    793                            ShapeUtil::HumanString(larger_shape).c_str());
    794   } else if (broadcast_dimensions.size() != ShapeUtil::Rank(smaller_shape)) {
    795     return InvalidArgument(
    796         "size of broadcast_dimensions has to match lower-rank operand's "
    797         "rank; "
    798         " lower-rank operand's rank is %lld, size of broadcast_dimensions is "
    799         "%zu",
    800         ShapeUtil::Rank(smaller_shape), broadcast_dimensions.size());
    801   }
    802 
    803   // broadcast_dimensions is a sequence of dimensions; its length is equal to
    804   // the rank of the lower-rank operand. The lower-rank operand's dimensions
    805   // have to be compatible with the higher-rank operand's dimensions at indices
    806   // specified by broadcast_dimensions. Here compatible means the dimension
    807   // sizes are equal or in one of the shapes the dimension size is
    808   // one. Examples:
    809   //
    810   // smaller_shape   larger_shape   broadcast_dimensions   output_shape
    811   //   []              [2, 3]          {}                    [2, 3]
    812   //   [3]             [4, 3]          {1}                   [4, 3]
    813   //   [2, 3]          [2, 3, 4]       {0, 1}                [2, 3, 4]
    814   //   [2, 1]          [2, 3, 4]       {0, 2}                [2, 3, 1]
    815   //   [2, 3]          [2, 1, 4]       {0, 1}                [2, 3, 4]
    816   //
    817   // The column output_shape may not be the final shape of the XLA
    818   // operation. After the "InDim" broadcasting implemented in this function
    819   // expands the rank, degenerate-dimension broadcasting (implemented in
    820   // InferDegenerateDimensionBroadcastShape) broadcasts dimensions of size one
    821   // up to match the dimension size of the other operand. For example, consider
    822   // the row in the table above with a smaller_shape of [2, 1]. The shape
    823   // returned by this function is [2, 3, 1] (output_shape) however, the result
    824   // shape of the XLA operation is [2, 3, 4] after degenerate-dimension
    825   // broadcasting.
    826   //
    827   // Invalid broadcasts:
    828   //
    829   // smaller_shape=[3], larger_shape=[4, 3], broadcast_dimensions={0}
    830   // Reason: Dimension zero** of larger_shape (size 4) is not compatible with
    831   //   dimension zero of smaller_shape(size 3). **Zero here comes from the value
    832   //   in broadcast_dimensions.
    833   //
    834   // smaller_shape=[2, 1], larger_shape=[2, 3, 4], broadcast_dimensions={1, 2}
    835   // Reason: Dimension one of larger_shape (size 3) is not compatible with
    836   //   dimension zero of smaller_shape(size 2)
    837 
    838   // The output shape is initially the larger_shape. Sizes of dimensions
    839   // specified in broadcast_dimensions are then changed to match the
    840   // corresponding dimension size in smaller_shape.
    841   Shape output_shape(larger_shape);
    842   output_shape.set_element_type(
    843       ShapeUtil::HigherPrecisionElementType(larger_shape, smaller_shape));
    844 
    845   for (int i = 0; i < smaller_shape.dimensions_size(); ++i) {
    846     int64 dimension_to_match = broadcast_dimensions.at(i);
    847     if (dimension_to_match < 0) {
    848       return InvalidArgument(
    849           "broadcast dimension number (%lld) cannot be negative",
    850           dimension_to_match);
    851     }
    852     if (dimension_to_match >= larger_shape.dimensions_size()) {
    853       return InvalidArgument(
    854           "broadcast dimension number (%lld) too large; higher-rank "
    855           "operand has rank %d",
    856           dimension_to_match, larger_shape.dimensions_size());
    857     }
    858     int64 small_dimension_size = smaller_shape.dimensions(i);
    859     int64 large_dimension_size = larger_shape.dimensions(dimension_to_match);
    860     // Dimension sizes must be compatible: match or be degenerate (degenerate
    861     // case is handled by degenerate dimension broadcasting which occurs after
    862     // InDim broadcasting).
    863     if (small_dimension_size != large_dimension_size &&
    864         small_dimension_size != 1 && large_dimension_size != 1) {
    865       return InvalidArgument(
    866           "broadcast dimension %d mismatch: %lld != %lld; %s and %s", i,
    867           small_dimension_size, large_dimension_size,
    868           ShapeUtil::HumanString(smaller_shape).c_str(),
    869           ShapeUtil::HumanString(larger_shape).c_str());
    870     }
    871     // Make sure the broadcast dimensions are listed in a strictly increasing
    872     // order.
    873     if (i > 0 && broadcast_dimensions.at(i - 1) >= dimension_to_match) {
    874       return InvalidArgument(
    875           "broadcast dimensions order is wrong: %lld comes after %lld",
    876           dimension_to_match, broadcast_dimensions.at(i - 1));
    877     }
    878 
    879     output_shape.set_dimensions(dimension_to_match, small_dimension_size);
    880   }
    881 
    882   return output_shape;
    883 }
    884 
    885 /* static */ StatusOr<Shape> ShapeInference::InferElementwiseBinaryOpShape(
    886     BinaryOperation operation, const Shape& lhs, const Shape& rhs,
    887     tensorflow::gtl::ArraySlice<int64> broadcast_dimensions) {
    888   TF_RETURN_IF_ERROR(
    889       ExpectNotTupleOrOpaque(lhs, "lhs of elementwise binary operation"));
    890   TF_RETURN_IF_ERROR(
    891       ExpectNotTupleOrOpaque(rhs, "rhs of elementwise binary operation"));
    892 
    893   if (!ShapeUtil::SameElementTypeIgnoringFpPrecision(lhs, rhs)) {
    894     return InvalidArgument(
    895         "binary op %s with different element types: %s and %s",
    896         BinaryOperation_Name(operation).c_str(),
    897         ShapeUtil::HumanString(lhs).c_str(),
    898         ShapeUtil::HumanString(rhs).c_str());
    899   }
    900 
    901   if (ShapeUtil::Rank(lhs) == ShapeUtil::Rank(rhs)) {
    902     std::vector<int64> identity_dims(ShapeUtil::Rank(lhs));
    903     std::iota(identity_dims.begin(), identity_dims.end(), 0);
    904     if (!broadcast_dimensions.empty() &&
    905         broadcast_dimensions != identity_dims) {
    906       return InvalidArgument(
    907           "broadcast dimensions field must either be not set or be the "
    908           "identity on binary operations with operands of the same rank");
    909     }
    910   }
    911 
    912   if (ShapeUtil::CompatibleIgnoringFpPrecision(lhs, rhs)) {
    913     // If the shapes are the same other than layout, the output shape is the
    914     // same (elementwise op).
    915     return ShapeUtil::ChangeElementType(
    916         lhs, ShapeUtil::HigherPrecisionElementType(lhs, rhs));
    917   }
    918 
    919   if (ShapeUtil::Rank(lhs) == ShapeUtil::Rank(rhs)) {
    920     return InferDegenerateDimensionBroadcastShape(operation, lhs, rhs);
    921   } else {
    922     // Ranks do not match, so perform InDim broadcasting using
    923     // broadcast_dimensions. Scalar broadcasting is a special case of this.
    924     const Shape& larger_shape =
    925         ShapeUtil::Rank(lhs) > ShapeUtil::Rank(rhs) ? lhs : rhs;
    926     const Shape& smaller_shape =
    927         ShapeUtil::Rank(lhs) > ShapeUtil::Rank(rhs) ? rhs : lhs;
    928 
    929     // After InDim broadcasting, perform degenerate dimensions broadcasting.
    930     TF_ASSIGN_OR_RETURN(
    931         Shape indim_broadcast_shape,
    932         InferInDimBroadcastShape(operation, smaller_shape, larger_shape,
    933                                  broadcast_dimensions));
    934 
    935     return InferDegenerateDimensionBroadcastShape(
    936         operation, indim_broadcast_shape, larger_shape);
    937   }
    938 }
    939 
    940 /* static */ StatusOr<Shape> ShapeInference::InferBinaryOpShape(
    941     HloOpcode opcode, const HloInstruction* lhs, const HloInstruction* rhs) {
    942   return InferBinaryOpShape(OpcodeToBinaryOperation(opcode), lhs->shape(),
    943                             rhs->shape(), /*broadcast_dimensions=*/{});
    944 }
    945 
    946 /* static */ StatusOr<Shape> ShapeInference::InferBinaryOpShape(
    947     BinaryOperation operation, const Shape& lhs, const Shape& rhs,
    948     tensorflow::gtl::ArraySlice<int64> broadcast_dimensions) {
    949   VLOG(2) << tensorflow::strings::Printf(
    950       "inferring shape for <%s>(%s, %s) with broadcast_dimensions={%s}",
    951       BinaryOperation_Name(operation).c_str(),
    952       ShapeUtil::HumanString(lhs).c_str(), ShapeUtil::HumanString(rhs).c_str(),
    953       Join(broadcast_dimensions, ", ").c_str());
    954   TF_DCHECK_OK(ShapeUtil::ValidateShapeWithOptionalLayout(lhs));
    955   TF_DCHECK_OK(ShapeUtil::ValidateShapeWithOptionalLayout(rhs));
    956 
    957   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(
    958       lhs, tensorflow::strings::StrCat("lhs of binary operation ",
    959                                        BinaryOperation_Name(operation))));
    960   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(
    961       rhs, tensorflow::strings::StrCat("rhs of binary operation ",
    962                                        BinaryOperation_Name(operation))));
    963   switch (operation) {
    964     case BINOP_MAX:
    965     case BINOP_MIN:
    966     case BINOP_SUB:
    967     case BINOP_ADD:
    968     case BINOP_ATAN2:
    969     case BINOP_POW:
    970     case BINOP_DIV:
    971     case BINOP_REM:
    972     case BINOP_MUL:
    973     case BINOP_SHIFT_LEFT:
    974     case BINOP_SHIFT_RIGHT_ARITHMETIC:
    975     case BINOP_SHIFT_RIGHT_LOGICAL:
    976       return InferElementwiseBinaryOpShape(operation, lhs, rhs,
    977                                            broadcast_dimensions);
    978 
    979     case BINOP_COMPLEX: {
    980       if (!ShapeUtil::ElementIsFloating(lhs)) {
    981         return InvalidArgument(
    982             "expected element type in shape to be floating for complex compose "
    983             "operation; got %s",
    984             PrimitiveType_Name(lhs.element_type()).c_str());
    985       }
    986       TF_ASSIGN_OR_RETURN(const Shape& shape,
    987                           InferElementwiseBinaryOpShape(operation, lhs, rhs,
    988                                                         broadcast_dimensions));
    989       if (lhs.element_type() == F32 && rhs.element_type() == F32) {
    990         return ShapeUtil::ChangeElementType(shape, C64);
    991       } else {
    992         return Unimplemented("complex component type not supported");
    993       }
    994     }
    995     case BINOP_AND:
    996     case BINOP_OR:
    997       if (lhs.element_type() != PRED &&
    998           !primitive_util::IsIntegralType(lhs.element_type())) {
    999         return InvalidArgument(
   1000             "expected pred or integral type in argument to and/or operation; "
   1001             "got %s",
   1002             PrimitiveType_Name(lhs.element_type()).c_str());
   1003       }
   1004       return InferElementwiseBinaryOpShape(operation, lhs, rhs,
   1005                                            broadcast_dimensions);
   1006     case BINOP_EQ:
   1007     case BINOP_GE:
   1008     case BINOP_GT:
   1009     case BINOP_LE:
   1010     case BINOP_LT:
   1011     case BINOP_NE: {
   1012       TF_ASSIGN_OR_RETURN(const Shape& shape,
   1013                           InferElementwiseBinaryOpShape(operation, lhs, rhs,
   1014                                                         broadcast_dimensions));
   1015       return ShapeUtil::ChangeElementType(shape, PRED);
   1016     }
   1017     default:
   1018       return Unimplemented(
   1019           "not yet implemented; infer binary op shape: %s; lhs: %s; rhs: %s",
   1020           BinaryOperation_Name(operation).c_str(),
   1021           lhs.ShortDebugString().c_str(), rhs.ShortDebugString().c_str());
   1022   }
   1023 }
   1024 
   1025 /* static */ StatusOr<Shape> ShapeInference::InferTernaryOpShape(
   1026     HloOpcode opcode, const HloInstruction* lhs, const HloInstruction* rhs,
   1027     const HloInstruction* ehs) {
   1028   return InferTernaryOpShape(OpcodeToTernaryOperation(opcode), lhs->shape(),
   1029                              rhs->shape(), ehs->shape());
   1030 }
   1031 
   1032 /* static */ StatusOr<Shape> ShapeInference::InferTernaryOpShape(
   1033     TernaryOperation operation, const Shape& lhs, const Shape& rhs,
   1034     const Shape& ehs) {
   1035   TF_DCHECK_OK(ShapeUtil::ValidateShapeWithOptionalLayout(lhs));
   1036   TF_DCHECK_OK(ShapeUtil::ValidateShapeWithOptionalLayout(rhs));
   1037   TF_DCHECK_OK(ShapeUtil::ValidateShapeWithOptionalLayout(ehs));
   1038   switch (operation) {
   1039     case TRIOP_CLAMP:
   1040       return InferClampShape(lhs, rhs, ehs);
   1041     case TRIOP_SELECT:
   1042       return InferSelectShape(lhs, rhs, ehs);
   1043     default:
   1044       return InvalidArgument("unknown operation %s",
   1045                              TernaryOperation_Name(operation).c_str());
   1046   }
   1047 }
   1048 
   1049 /* static */ StatusOr<Shape> ShapeInference::InferVariadicOpShape(
   1050     HloOpcode opcode,
   1051     tensorflow::gtl::ArraySlice<const HloInstruction*> operands) {
   1052   std::vector<const Shape*> operand_shapes;
   1053   for (const HloInstruction* operand : operands) {
   1054     operand_shapes.push_back(&operand->shape());
   1055   }
   1056   return InferVariadicOpShape(OpcodeToVariadicOperation(opcode),
   1057                               operand_shapes);
   1058 }
   1059 
   1060 /* static */ StatusOr<Shape> ShapeInference::InferVariadicOpShape(
   1061     VariadicOperation operation,
   1062     tensorflow::gtl::ArraySlice<const Shape*> operand_shapes) {
   1063   for (const Shape* shape : operand_shapes) {
   1064     TF_DCHECK_OK(ShapeUtil::ValidateShapeWithOptionalLayout(*shape));
   1065   }
   1066   switch (operation) {
   1067     case VAROP_TUPLE: {
   1068       Shape result = ShapeUtil::MakeTupleShape({});
   1069       for (const Shape* shape : operand_shapes) {
   1070         ShapeUtil::AppendShapeToTuple(*shape, &result);
   1071       }
   1072       return result;
   1073     }
   1074     default:
   1075       return InvalidArgument("unknown operation %s",
   1076                              VariadicOperation_Name(operation).c_str());
   1077   }
   1078 }
   1079 
   1080 /* static */ StatusOr<Shape> ShapeInference::InferMapShape(
   1081     tensorflow::gtl::ArraySlice<const Shape*> arg_shapes,
   1082     const ProgramShape& to_apply,
   1083     tensorflow::gtl::ArraySlice<int64> dimensions) {
   1084   if (arg_shapes.empty()) {
   1085     return InvalidArgument("Map expects at least one argument");
   1086   }
   1087 
   1088   // All arguments must have the same shape.
   1089   const Shape* arg_shape = arg_shapes[0];
   1090   for (size_t i = 1; i < arg_shapes.size(); ++i) {
   1091     TF_RETURN_IF_ERROR(
   1092         ExpectNotTupleOrOpaque(*arg_shapes[i], "operand of map"));
   1093 
   1094     if (ShapeUtil::CompatibleIgnoringFpPrecision(*arg_shapes[i], *arg_shape)) {
   1095       continue;
   1096     }
   1097     if (!ShapeUtil::IsTuple(*arg_shapes[i]) &&
   1098         !ShapeUtil::IsTuple(*arg_shape) &&
   1099         ShapeUtil::SameElementTypeIgnoringFpPrecision(*arg_shapes[i],
   1100                                                       *arg_shape)) {
   1101       if (ShapeUtil::IsScalar(*arg_shapes[i])) {
   1102         continue;
   1103       }
   1104       if (ShapeUtil::IsScalar(*arg_shape)) {
   1105         arg_shape = arg_shapes[i];
   1106         continue;
   1107       }
   1108     }
   1109 
   1110     std::vector<string> pieces;
   1111     for (const Shape* shape : arg_shapes) {
   1112       pieces.push_back(ShapeUtil::HumanString(*shape));
   1113     }
   1114     return InvalidArgument(
   1115         "Map operation requires all operands to have the same shape; got: "
   1116         "%s",
   1117         Join(pieces, ", ").c_str());
   1118   }
   1119 
   1120   // Check that dimensions.size == arg_shape.dimensions_size() (we currently
   1121   // only support mapping across all dimensions: i.e. scalar map functions).
   1122   if (dimensions.size() != arg_shape->dimensions_size()) {
   1123     return InvalidArgument(
   1124         "Map applied to a subset of dimensions currently not supported: "
   1125         "arg_dimension_size: %d, requested_map_dimensions_size: %zu",
   1126         arg_shape->dimensions_size(), dimensions.size());
   1127   }
   1128 
   1129   // Check that requested map dimensions numbers are monotonically increasing.
   1130   for (int i = 0; i < dimensions.size(); ++i) {
   1131     if (dimensions[i] != i) {
   1132       return InvalidArgument(
   1133           "Map requires monotonically increasing dimension numbers, found: %s ",
   1134           Join(dimensions, ", ").c_str());
   1135     }
   1136   }
   1137 
   1138   // The applied function's arity equals the number of arguments.
   1139   if (arg_shapes.size() != to_apply.parameters_size()) {
   1140     return InvalidArgument(
   1141         "Map applied function arity must match number of arguments; got: "
   1142         "arity: %d, arguments: %zu",
   1143         to_apply.parameters_size(), arg_shapes.size());
   1144   }
   1145 
   1146   // The parameters should all be scalars, and the output too.
   1147   const Shape& output_shape = to_apply.result();
   1148   if (!ShapeUtil::IsScalar(output_shape)) {
   1149     return InvalidArgument(
   1150         "mapped computation's result has to be a scalar; "
   1151         "got: %s",
   1152         ShapeUtil::HumanString(output_shape).c_str());
   1153   }
   1154 
   1155   for (int i = 0; i < to_apply.parameters_size(); ++i) {
   1156     const Shape& parameter_shape = to_apply.parameters(i);
   1157 
   1158     if (!ShapeUtil::IsScalar(parameter_shape)) {
   1159       return InvalidArgument(
   1160           "mapped computation's parameter has to be a scalar; "
   1161           "got parameter %d shape: %s",
   1162           i, ShapeUtil::HumanString(parameter_shape).c_str());
   1163     }
   1164 
   1165     if (!ShapeUtil::SameElementTypeIgnoringFpPrecision(parameter_shape,
   1166                                                        *arg_shape)) {
   1167       return InvalidArgument(
   1168           "mapped computation's parameter type has to match argument element "
   1169           "type; got parameter %d shape: %s, argument shape: %s",
   1170           i, ShapeUtil::HumanString(parameter_shape).c_str(),
   1171           ShapeUtil::HumanString(*arg_shape).c_str());
   1172     }
   1173   }
   1174 
   1175   return ShapeUtil::MakeShape(output_shape.element_type(),
   1176                               AsInt64Slice(arg_shape->dimensions()));
   1177 }
   1178 
   1179 /* static */ StatusOr<Shape> ShapeInference::InferBatchNormTrainingShape(
   1180     const Shape& operand_shape, const Shape& scale_shape,
   1181     const Shape& offset_shape, int64 feature_index) {
   1182   TF_RETURN_IF_ERROR(
   1183       ExpectNotTupleOrOpaque(operand_shape, "operand of batch norm training"));
   1184   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(
   1185       offset_shape, "offset input of batch norm training"));
   1186   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(
   1187       scale_shape, "scale input of batch norm training"));
   1188 
   1189   TF_RET_CHECK(ShapeUtil::ValidateShapeWithOptionalLayout(operand_shape) ==
   1190                tensorflow::Status::OK());
   1191   TF_RET_CHECK(ShapeUtil::ValidateShapeWithOptionalLayout(offset_shape) ==
   1192                tensorflow::Status::OK());
   1193   TF_RET_CHECK(ShapeUtil::ValidateShapeWithOptionalLayout(scale_shape) ==
   1194                tensorflow::Status::OK());
   1195 
   1196   if (feature_index >= ShapeUtil::Rank(operand_shape)) {
   1197     return InvalidArgument(
   1198         "Expected feature_index of batch-norm-training to be "
   1199         "smaller than the rank of operand_shape; "
   1200         "got feature_index %lld, and rank %lld",
   1201         feature_index, ShapeUtil::Rank(operand_shape));
   1202   }
   1203 
   1204   if (feature_index < 0) {
   1205     return InvalidArgument(
   1206         "Expected feature_index of batch-norm-training to "
   1207         "be a non-negative number, got %lld",
   1208         feature_index);
   1209   }
   1210 
   1211   if (ShapeUtil::Rank(operand_shape) < 1) {
   1212     return InvalidArgument(
   1213         "Expected the rank of operand to "
   1214         "batch-norm-training to be at least 1; got %lld",
   1215         ShapeUtil::Rank(operand_shape));
   1216   }
   1217 
   1218   if (ShapeUtil::Rank(offset_shape) != 1) {
   1219     return InvalidArgument(
   1220         "Offset input of batch-norm-training must have"
   1221         " rank 1, but has rank %lld.",
   1222         ShapeUtil::Rank(offset_shape));
   1223   }
   1224 
   1225   if (ShapeUtil::Rank(scale_shape) != 1) {
   1226     return InvalidArgument(
   1227         "Scale input of batch-norm-training must have"
   1228         " rank 1, but has rank %lld.",
   1229         ShapeUtil::Rank(scale_shape));
   1230   }
   1231 
   1232   if (!ShapeUtil::ElementIsFloating(operand_shape)) {
   1233     return InvalidArgument(
   1234         "The operand to batch-norm-training must have a floating point "
   1235         "element type, but the shape is %s",
   1236         PrimitiveType_Name(operand_shape.element_type()).c_str());
   1237   }
   1238 
   1239   if (!ShapeUtil::SameElementTypeIgnoringFpPrecision(offset_shape,
   1240                                                      operand_shape)) {
   1241     return InvalidArgument(
   1242         "The inputs should have the same element type for batch-norm-training, "
   1243         "but the shape of offset factor is %s "
   1244         "and the shape of operand is %s",
   1245         PrimitiveType_Name(offset_shape.element_type()).c_str(),
   1246         PrimitiveType_Name(operand_shape.element_type()).c_str());
   1247   }
   1248 
   1249   if (!ShapeUtil::SameElementTypeIgnoringFpPrecision(scale_shape,
   1250                                                      operand_shape)) {
   1251     return InvalidArgument(
   1252         "The inputs should have the same element type for batch-norm-training, "
   1253         "but the shape of scale factor is %s "
   1254         "and the shape of operand is %s",
   1255         PrimitiveType_Name(scale_shape.element_type()).c_str(),
   1256         PrimitiveType_Name(operand_shape.element_type()).c_str());
   1257   }
   1258 
   1259   const int64 feature_count = operand_shape.dimensions(feature_index);
   1260   Shape output_shape_for_mean_and_var =
   1261       ShapeUtil::MakeShape(operand_shape.element_type(), {feature_count});
   1262 
   1263   if (ShapeUtil::GetDimension(offset_shape, 0) != feature_count) {
   1264     return InvalidArgument(
   1265         "The size of offset factor should be the same as feature count,"
   1266         "but the size of offset factor is %lld "
   1267         "and the feature count is %lld",
   1268         ShapeUtil::GetDimension(offset_shape, 0), feature_count);
   1269   }
   1270 
   1271   if (ShapeUtil::GetDimension(scale_shape, 0) != feature_count) {
   1272     return InvalidArgument(
   1273         "The size of scale factor should be the same as feature count,"
   1274         "but the size of scale factor is %lld "
   1275         "and the feature count is %lld",
   1276         ShapeUtil::GetDimension(scale_shape, 0), feature_count);
   1277   }
   1278 
   1279   return ShapeUtil::MakeTupleShape({operand_shape,
   1280                                     output_shape_for_mean_and_var,
   1281                                     output_shape_for_mean_and_var});
   1282 }
   1283 
   1284 /* static */ StatusOr<Shape> ShapeInference::InferBatchNormInferenceShape(
   1285     const Shape& operand_shape, const Shape& scale_shape,
   1286     const Shape& offset_shape, const Shape& mean_shape,
   1287     const Shape& variance_shape, int64 feature_index) {
   1288   TF_RETURN_IF_ERROR(
   1289       ExpectNotTupleOrOpaque(operand_shape, "operand of batch norm inference"));
   1290   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(
   1291       offset_shape, "offset input of batch norm inference"));
   1292   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(
   1293       scale_shape, "scale input of batch norm inference"));
   1294 
   1295   TF_RET_CHECK(ShapeUtil::ValidateShapeWithOptionalLayout(operand_shape) ==
   1296                tensorflow::Status::OK());
   1297   TF_RET_CHECK(ShapeUtil::ValidateShapeWithOptionalLayout(offset_shape) ==
   1298                tensorflow::Status::OK());
   1299   TF_RET_CHECK(ShapeUtil::ValidateShapeWithOptionalLayout(scale_shape) ==
   1300                tensorflow::Status::OK());
   1301   TF_RET_CHECK(ShapeUtil::ValidateShapeWithOptionalLayout(mean_shape) ==
   1302                tensorflow::Status::OK());
   1303   TF_RET_CHECK(ShapeUtil::ValidateShapeWithOptionalLayout(variance_shape) ==
   1304                tensorflow::Status::OK());
   1305 
   1306   if (feature_index >= ShapeUtil::Rank(operand_shape)) {
   1307     return InvalidArgument(
   1308         "Expected feature_index of batch-norm-inference to be "
   1309         "smaller than the rank of operand_shape; "
   1310         "got feature_index %lld, and rank %lld",
   1311         feature_index, ShapeUtil::Rank(operand_shape));
   1312   }
   1313 
   1314   if (feature_index < 0) {
   1315     return InvalidArgument(
   1316         "Expected feature_index of batch-norm-inference to "
   1317         "be a non-negative number, got %lld",
   1318         feature_index);
   1319   }
   1320 
   1321   if (ShapeUtil::Rank(operand_shape) < 1) {
   1322     return InvalidArgument(
   1323         "Expected the rank of operand to "
   1324         "batch-norm-inference to be at least 1; got %lld",
   1325         ShapeUtil::Rank(operand_shape));
   1326   }
   1327 
   1328   if (ShapeUtil::Rank(offset_shape) != 1) {
   1329     return InvalidArgument(
   1330         "Offset input of batch-norm-inference must have"
   1331         " rank 1, but has rank %lld.",
   1332         ShapeUtil::Rank(offset_shape));
   1333   }
   1334 
   1335   if (ShapeUtil::Rank(scale_shape) != 1) {
   1336     return InvalidArgument(
   1337         "Scale input of batch-norm-inference must have"
   1338         " rank 1, but has rank %lld.",
   1339         ShapeUtil::Rank(scale_shape));
   1340   }
   1341 
   1342   if (!ShapeUtil::ElementIsFloating(operand_shape)) {
   1343     return InvalidArgument(
   1344         "The operand to batch-norm-inference must have a floating point "
   1345         "element type, but the shape is %s",
   1346         PrimitiveType_Name(operand_shape.element_type()).c_str());
   1347   }
   1348 
   1349   if (!ShapeUtil::SameElementTypeIgnoringFpPrecision(offset_shape,
   1350                                                      operand_shape)) {
   1351     return InvalidArgument(
   1352         "The inputs should have the same element type for "
   1353         "batch-norm-inference, "
   1354         "but the shape of offset factor is %s "
   1355         "and the shape of operand is %s",
   1356         PrimitiveType_Name(offset_shape.element_type()).c_str(),
   1357         PrimitiveType_Name(operand_shape.element_type()).c_str());
   1358   }
   1359 
   1360   if (!ShapeUtil::SameElementTypeIgnoringFpPrecision(scale_shape,
   1361                                                      operand_shape)) {
   1362     return InvalidArgument(
   1363         "The inputs should have the same element type for "
   1364         "batch-norm-inference, "
   1365         "but the shape of scale factor is %s "
   1366         "and the shape of operand is %s",
   1367         PrimitiveType_Name(scale_shape.element_type()).c_str(),
   1368         PrimitiveType_Name(operand_shape.element_type()).c_str());
   1369   }
   1370 
   1371   if (!ShapeUtil::SameElementTypeIgnoringFpPrecision(mean_shape,
   1372                                                      operand_shape)) {
   1373     return InvalidArgument(
   1374         "The inputs should have the same element type for "
   1375         "batch-norm-inference, "
   1376         "but the shape of mean is %s "
   1377         "and the shape of operand is %s",
   1378         PrimitiveType_Name(mean_shape.element_type()).c_str(),
   1379         PrimitiveType_Name(operand_shape.element_type()).c_str());
   1380   }
   1381 
   1382   if (!ShapeUtil::SameElementTypeIgnoringFpPrecision(variance_shape,
   1383                                                      operand_shape)) {
   1384     return InvalidArgument(
   1385         "The inputs should have the same element type for "
   1386         "batch-norm-inference, "
   1387         "but the shape of variance is %s "
   1388         "and the shape of operand is %s",
   1389         PrimitiveType_Name(mean_shape.element_type()).c_str(),
   1390         PrimitiveType_Name(variance_shape.element_type()).c_str());
   1391   }
   1392 
   1393   const int64 feature_count = operand_shape.dimensions(feature_index);
   1394   Shape output_shape_for_mean_and_var =
   1395       ShapeUtil::MakeShape(operand_shape.element_type(), {feature_count});
   1396 
   1397   if (ShapeUtil::GetDimension(offset_shape, 0) != feature_count) {
   1398     return InvalidArgument(
   1399         "The size of offset factor should be the same as feature count,"
   1400         "but the size of offset factor is %lld "
   1401         "and the feature count is %lld",
   1402         ShapeUtil::GetDimension(offset_shape, 0), feature_count);
   1403   }
   1404 
   1405   if (ShapeUtil::GetDimension(scale_shape, 0) != feature_count) {
   1406     return InvalidArgument(
   1407         "The size of scale factor should be the same as feature count,"
   1408         "but the size of scale factor is %lld "
   1409         "and the feature count is %lld",
   1410         ShapeUtil::GetDimension(scale_shape, 0), feature_count);
   1411   }
   1412 
   1413   if (ShapeUtil::GetDimension(mean_shape, 0) != feature_count) {
   1414     return InvalidArgument(
   1415         "The size of mean should be the same as feature count,"
   1416         "but the size of mean is %lld "
   1417         "and the feature count is %lld",
   1418         ShapeUtil::GetDimension(mean_shape, 0), feature_count);
   1419   }
   1420 
   1421   if (ShapeUtil::GetDimension(variance_shape, 0) != feature_count) {
   1422     return InvalidArgument(
   1423         "The size of variance should be the same as feature count,"
   1424         "but the size of variance is %lld "
   1425         "and the feature count is %lld",
   1426         ShapeUtil::GetDimension(variance_shape, 0), feature_count);
   1427   }
   1428 
   1429   return operand_shape;
   1430 }
   1431 
   1432 /* static */ StatusOr<Shape> ShapeInference::InferBatchNormGradShape(
   1433     const Shape& operand_shape, const Shape& scale_shape,
   1434     const Shape& mean_shape, const Shape& var_shape,
   1435     const Shape& output_grad_shape, int64 feature_index) {
   1436   TF_RETURN_IF_ERROR(
   1437       ExpectNotTupleOrOpaque(operand_shape, "operand of batch norm grad"));
   1438   TF_RETURN_IF_ERROR(
   1439       ExpectNotTupleOrOpaque(scale_shape, "scale input of batch norm grad"));
   1440   TF_RETURN_IF_ERROR(
   1441       ExpectNotTupleOrOpaque(mean_shape, "mean input of batch norm grad"));
   1442   TF_RETURN_IF_ERROR(
   1443       ExpectNotTupleOrOpaque(var_shape, "var input of batch norm grad"));
   1444   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(
   1445       output_grad_shape, "output_grad input of batch norm grad"));
   1446 
   1447   TF_RETURN_IF_ERROR(ShapeUtil::ValidateShapeWithOptionalLayout(operand_shape));
   1448   TF_RETURN_IF_ERROR(ShapeUtil::ValidateShapeWithOptionalLayout(mean_shape));
   1449   TF_RETURN_IF_ERROR(ShapeUtil::ValidateShapeWithOptionalLayout(scale_shape));
   1450   TF_RETURN_IF_ERROR(ShapeUtil::ValidateShapeWithOptionalLayout(var_shape));
   1451   TF_RETURN_IF_ERROR(
   1452       ShapeUtil::ValidateShapeWithOptionalLayout(output_grad_shape));
   1453 
   1454   if (feature_index >= ShapeUtil::Rank(operand_shape)) {
   1455     return InvalidArgument(
   1456         "Expected feature_index of batch-norm-grad to be "
   1457         "smaller than the rank of operand_shape; "
   1458         "got feature_index %lld, and rank %lld",
   1459         feature_index, ShapeUtil::Rank(operand_shape));
   1460   }
   1461 
   1462   if (ShapeUtil::Rank(operand_shape) != ShapeUtil::Rank(output_grad_shape)) {
   1463     return InvalidArgument(
   1464         "Expected operand_shape of batch-norm-grad to have the same rank as"
   1465         " output_grad_shape; got rank(oprand_shape) %lld, and"
   1466         " rank(output_grad_shape) %lld",
   1467         ShapeUtil::Rank(operand_shape), ShapeUtil::Rank(output_grad_shape));
   1468   }
   1469 
   1470   if (ShapeUtil::Rank(mean_shape) != 1) {
   1471     return InvalidArgument(
   1472         "Mean input of batch-norm-grad must have"
   1473         " rank 1, but has rank %lld.",
   1474         ShapeUtil::Rank(mean_shape));
   1475   }
   1476 
   1477   if (ShapeUtil::Rank(scale_shape) != 1) {
   1478     return InvalidArgument(
   1479         "Scale input of batch-norm-grad must have"
   1480         " rank 1, but has rank %lld.",
   1481         ShapeUtil::Rank(scale_shape));
   1482   }
   1483 
   1484   if (ShapeUtil::Rank(var_shape) != 1) {
   1485     return InvalidArgument(
   1486         "Var input of batch-norm-grad must have"
   1487         " rank 1, but has rank %lld.",
   1488         ShapeUtil::Rank(var_shape));
   1489   }
   1490 
   1491   if (!ShapeUtil::ElementIsFloating(operand_shape)) {
   1492     return InvalidArgument(
   1493         "The operand to batch-norm-grad must have a floating point "
   1494         "element type, but the shape is %s",
   1495         PrimitiveType_Name(operand_shape.element_type()).c_str());
   1496   }
   1497 
   1498   if (!ShapeUtil::ElementIsFloating(output_grad_shape)) {
   1499     return InvalidArgument(
   1500         "The output_grad to batch-norm-grad must have a floating point "
   1501         "element type, but the shape is %s",
   1502         PrimitiveType_Name(output_grad_shape.element_type()).c_str());
   1503   }
   1504 
   1505   if (!ShapeUtil::SameElementTypeIgnoringFpPrecision(output_grad_shape,
   1506                                                      operand_shape)) {
   1507     return InvalidArgument(
   1508         "The inputs should have the same element type for batch-norm-grad, "
   1509         "but the element type of output_grad is %s "
   1510         "and the element type of operand is %s",
   1511         PrimitiveType_Name(output_grad_shape.element_type()).c_str(),
   1512         PrimitiveType_Name(operand_shape.element_type()).c_str());
   1513   }
   1514 
   1515   if (!ShapeUtil::SameElementTypeIgnoringFpPrecision(scale_shape,
   1516                                                      operand_shape)) {
   1517     return InvalidArgument(
   1518         "The inputs should have the same element type for batch-norm-grad, "
   1519         "but the element type of scale factor is %s "
   1520         "and the element type of operand is %s",
   1521         PrimitiveType_Name(scale_shape.element_type()).c_str(),
   1522         PrimitiveType_Name(operand_shape.element_type()).c_str());
   1523   }
   1524 
   1525   if (!ShapeUtil::SameElementTypeIgnoringFpPrecision(mean_shape,
   1526                                                      operand_shape)) {
   1527     return InvalidArgument(
   1528         "The inputs should have the same element type for batch-norm-grad, "
   1529         "but the element type of mean is %s "
   1530         "and the element type of operand is %s",
   1531         PrimitiveType_Name(mean_shape.element_type()).c_str(),
   1532         PrimitiveType_Name(operand_shape.element_type()).c_str());
   1533   }
   1534 
   1535   if (!ShapeUtil::SameElementTypeIgnoringFpPrecision(var_shape,
   1536                                                      operand_shape)) {
   1537     return InvalidArgument(
   1538         "The inputs should have the same element type for batch-norm-grad, "
   1539         "but the element type of mean is %s "
   1540         "and the element type of operand is %s",
   1541         PrimitiveType_Name(mean_shape.element_type()).c_str(),
   1542         PrimitiveType_Name(operand_shape.element_type()).c_str());
   1543   }
   1544 
   1545   const int64 feature_count = operand_shape.dimensions(feature_index);
   1546 
   1547   Shape feature_shape =
   1548       ShapeUtil::MakeShape(operand_shape.element_type(), {feature_count});
   1549 
   1550   if (ShapeUtil::GetDimension(mean_shape, 0) != feature_count) {
   1551     return InvalidArgument(
   1552         "The size of mean should be the same as feature count,"
   1553         "but the size of offset factor is %lld "
   1554         "and the feature count is %lld",
   1555         ShapeUtil::GetDimension(mean_shape, 0), feature_count);
   1556   }
   1557 
   1558   if (ShapeUtil::GetDimension(scale_shape, 0) != feature_count) {
   1559     return InvalidArgument(
   1560         "The size of scale factor should be the same as feature count,"
   1561         "but the size of scale factor is %lld "
   1562         "and the feature count is %lld",
   1563         ShapeUtil::GetDimension(scale_shape, 0), feature_count);
   1564   }
   1565 
   1566   if (ShapeUtil::GetDimension(var_shape, 0) != feature_count) {
   1567     return InvalidArgument(
   1568         "The size of variance should be the same as feature count,"
   1569         "but the size of variance is %lld "
   1570         "and the feature count is %lld",
   1571         ShapeUtil::GetDimension(var_shape, 0), feature_count);
   1572   }
   1573 
   1574   // Verify operand_shape and output_grad_shape have same bounds.
   1575   for (int64 i = 0; i < ShapeUtil::Rank(operand_shape); ++i) {
   1576     if (ShapeUtil::GetDimension(operand_shape, i) !=
   1577         ShapeUtil::GetDimension(output_grad_shape, i)) {
   1578       return InvalidArgument(
   1579           "The bounds of operand shape should be the same as output_grad's,"
   1580           "but the bound of operand_shape at dimension %lld is %lld "
   1581           "and the bound of output_grad_shape is %lld",
   1582           i, ShapeUtil::GetDimension(operand_shape, i),
   1583           ShapeUtil::GetDimension(output_grad_shape, i));
   1584     }
   1585   }
   1586 
   1587   return ShapeUtil::MakeTupleShape(
   1588       {operand_shape, feature_shape, feature_shape});
   1589 }
   1590 
   1591 /* static */ StatusOr<Shape> ShapeInference::InferConvolveShape(
   1592     const Shape& lhs, const Shape& rhs, const Window& window,
   1593     const ConvolutionDimensionNumbers& dnums) {
   1594   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(lhs, "lhs of convolution"));
   1595   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(rhs, "rhs of convolution"));
   1596 
   1597   if (!ShapeUtil::SameElementTypeIgnoringFpPrecision(lhs, rhs)) {
   1598     return InvalidArgument(
   1599         "Convolution with different element types: %s and %s",
   1600         ShapeUtil::HumanString(lhs).c_str(),
   1601         ShapeUtil::HumanString(rhs).c_str());
   1602   }
   1603   if (dnums.input_spatial_dimensions_size() !=
   1604       dnums.kernel_spatial_dimensions_size()) {
   1605     return InvalidArgument(
   1606         "Both arguments to convolution must have same number of dimensions.\n"
   1607         "Window: %s",
   1608         window.DebugString().c_str());
   1609   }
   1610 
   1611   const int num_spatial_dims = dnums.input_spatial_dimensions_size();
   1612   if (window.dimensions_size() != num_spatial_dims) {
   1613     return InvalidArgument(
   1614         "Window must have same number of dimensions as dimension numbers.\n"
   1615         "Window: %s\nDimension numbers: %s",
   1616         window.DebugString().c_str(), dnums.DebugString().c_str());
   1617   }
   1618 
   1619   const int num_dims = num_spatial_dims + 2;
   1620   if (ShapeUtil::Rank(lhs) != num_dims) {
   1621     return InvalidArgument(
   1622         "The LHS argument to a convolution should have rank %d.\n"
   1623         "lhs: %s",
   1624         num_dims, ShapeUtil::HumanString(lhs).c_str());
   1625   }
   1626   if (ShapeUtil::Rank(rhs) != num_dims) {
   1627     return InvalidArgument(
   1628         "The RHS argument to a convolution should have rank %d.\n"
   1629         "lhs: %s",
   1630         num_dims, ShapeUtil::HumanString(lhs).c_str());
   1631   }
   1632   TF_DCHECK_OK(ShapeUtil::ValidateShapeWithOptionalLayout(lhs));
   1633   TF_DCHECK_OK(ShapeUtil::ValidateShapeWithOptionalLayout(rhs));
   1634 
   1635   // Verifies that the input and window dimensions are a permutation of
   1636   // the dimension numbers.
   1637   std::vector<int64> input_dnums(num_dims);
   1638   input_dnums[0] = dnums.input_batch_dimension();
   1639   input_dnums[1] = dnums.input_feature_dimension();
   1640   std::copy(dnums.input_spatial_dimensions().begin(),
   1641             dnums.input_spatial_dimensions().end(), input_dnums.begin() + 2);
   1642   std::sort(input_dnums.begin(), input_dnums.end());
   1643 
   1644   std::vector<int64> window_dnums(num_dims);
   1645   window_dnums[0] = dnums.kernel_input_feature_dimension();
   1646   window_dnums[1] = dnums.kernel_output_feature_dimension();
   1647   std::copy(dnums.kernel_spatial_dimensions().begin(),
   1648             dnums.kernel_spatial_dimensions().end(), window_dnums.begin() + 2);
   1649   std::sort(window_dnums.begin(), window_dnums.end());
   1650 
   1651   std::vector<int64> output_dnums(num_dims);
   1652   output_dnums[0] = dnums.output_batch_dimension();
   1653   output_dnums[1] = dnums.output_feature_dimension();
   1654   std::copy(dnums.output_spatial_dimensions().begin(),
   1655             dnums.output_spatial_dimensions().end(), output_dnums.begin() + 2);
   1656   std::sort(output_dnums.begin(), output_dnums.end());
   1657 
   1658   std::vector<int64> expected_dnums(num_dims);
   1659   std::iota(expected_dnums.begin(), expected_dnums.end(), 0);
   1660 
   1661   const auto in_range = [num_dims](int64 i) { return 0 <= i && i < num_dims; };
   1662   if (!std::all_of(input_dnums.begin(), input_dnums.end(), in_range) ||
   1663       !std::all_of(window_dnums.begin(), window_dnums.end(), in_range) ||
   1664       !std::all_of(output_dnums.begin(), output_dnums.end(), in_range)) {
   1665     return InvalidArgument(
   1666         "A dimension number is out of range in convolution: %s",
   1667         dnums.DebugString().c_str());
   1668   }
   1669 
   1670   if (input_dnums != expected_dnums) {
   1671     return InvalidArgument(
   1672         "Input dimensions of convolution must contain each dimension exactly "
   1673         "once: %s",
   1674         dnums.DebugString().c_str());
   1675   }
   1676   if (window_dnums != expected_dnums) {
   1677     return InvalidArgument(
   1678         "Window dimensions of convolution must contain each dimension exactly "
   1679         "once: %s",
   1680         dnums.DebugString().c_str());
   1681   }
   1682   if (output_dnums != expected_dnums) {
   1683     return InvalidArgument(
   1684         "Output dimensions of convolution must contain each dimension exactly "
   1685         "once: %s",
   1686         dnums.DebugString().c_str());
   1687   }
   1688 
   1689   std::vector<int64> input_spatial_dims(num_spatial_dims);
   1690   for (int i = 0; i < num_spatial_dims; ++i) {
   1691     input_spatial_dims[i] = lhs.dimensions(dnums.input_spatial_dimensions(i));
   1692   }
   1693   const int64 input_features = lhs.dimensions(dnums.input_feature_dimension());
   1694   const int64 input_batch = lhs.dimensions(dnums.input_batch_dimension());
   1695 
   1696   std::vector<int64> kernel_spatial_dims(num_spatial_dims);
   1697   for (int i = 0; i < num_spatial_dims; ++i) {
   1698     kernel_spatial_dims[i] = rhs.dimensions(dnums.kernel_spatial_dimensions(i));
   1699   }
   1700   const int64 kernel_input_features =
   1701       rhs.dimensions(dnums.kernel_input_feature_dimension());
   1702   const int64 kernel_output_features =
   1703       rhs.dimensions(dnums.kernel_output_feature_dimension());
   1704 
   1705   if (input_features != kernel_input_features) {
   1706     return InvalidArgument(
   1707         "Expected LHS feature dimension (value %lld) to match RHS "
   1708         "input feature dimension (value %lld); got <conv>(%s, %s)\n"
   1709         "Dimension numbers: {%s}",
   1710         input_features, kernel_input_features,
   1711         ShapeUtil::HumanString(lhs).c_str(),
   1712         ShapeUtil::HumanString(rhs).c_str(), dnums.DebugString().c_str());
   1713   }
   1714   std::vector<int64> window_dims(num_spatial_dims);
   1715   for (int i = 0; i < num_spatial_dims; ++i) {
   1716     window_dims[i] = window.dimensions(i).size();
   1717   }
   1718   if (kernel_spatial_dims != window_dims) {
   1719     return InvalidArgument(
   1720         "Window dimensions do not match RHS shape:\n\t"
   1721         "RHS shape: %s\n\t"
   1722         "Window: {%s}\n\t"
   1723         "Dimension numbers: {%s}",
   1724         ShapeUtil::HumanString(rhs).c_str(), window.ShortDebugString().c_str(),
   1725         dnums.ShortDebugString().c_str());
   1726   }
   1727 
   1728   Shape base_shape =
   1729       ShapeUtil::MakeShape(lhs.element_type(), input_spatial_dims);
   1730   TF_ASSIGN_OR_RETURN(
   1731       Shape window_output_shape,
   1732       InferWindowOutputShape(base_shape, window, lhs.element_type(),
   1733                              /*allow_negative_padding=*/true));
   1734 
   1735   std::vector<int64> dimensions(num_dims);
   1736   dimensions[dnums.output_batch_dimension()] = input_batch;
   1737   dimensions[dnums.output_feature_dimension()] = kernel_output_features;
   1738   for (int i = 0; i < num_spatial_dims; ++i) {
   1739     dimensions[dnums.output_spatial_dimensions(i)] =
   1740         window_output_shape.dimensions(i);
   1741   }
   1742   return ShapeUtil::MakeShape(ShapeUtil::HigherPrecisionElementType(lhs, rhs),
   1743                               dimensions);
   1744 }
   1745 
   1746 /* static */ StatusOr<Shape> ShapeInference::InferFftShape(
   1747     const Shape& in, const FftType fft_type,
   1748     const tensorflow::gtl::ArraySlice<int64> fft_length) {
   1749   const int64 fft_rank = fft_length.size();
   1750   if (fft_rank < 1 || fft_rank > 3) {
   1751     return InvalidArgument("FFT only supports ranks 1-3, but got %lld",
   1752                            fft_rank);
   1753   }
   1754 #define RET_CHECK_RANK(x)                              \
   1755   if (x.dimensions_size() < fft_rank) {                \
   1756     return InvalidArgument(                            \
   1757         "FFT of rank %lld requires input of at least " \
   1758         "same rank; got input of rank %d",             \
   1759         fft_rank, x.dimensions_size());                \
   1760   }
   1761   switch (fft_type) {
   1762     case FFT:
   1763     case IFFT:
   1764       if (in.element_type() != C64) {
   1765         return InvalidArgument("%s requires C64 input type, found %s",
   1766                                FftType_Name(fft_type).c_str(),
   1767                                PrimitiveType_Name(in.element_type()).c_str());
   1768       }
   1769       RET_CHECK_RANK(in);
   1770       return in;
   1771     case RFFT: {
   1772       if (in.element_type() != F32) {
   1773         return InvalidArgument("RFFT requires F32 input type, found %s",
   1774                                PrimitiveType_Name(in.element_type()).c_str());
   1775       }
   1776       RET_CHECK_RANK(in);
   1777       for (int i = 0; i < fft_rank; i++) {
   1778         if (in.dimensions(in.dimensions_size() - fft_rank + i) !=
   1779             fft_length[i]) {
   1780           return InvalidArgument(
   1781               "RFFT requires innermost dimensions match fft_length but "
   1782               "dimension %lld is %lld and should be %lld",
   1783               in.dimensions_size() - fft_rank + i,
   1784               in.dimensions(in.dimensions_size() - fft_rank + i),
   1785               fft_length[i]);
   1786         }
   1787       }
   1788       Shape result = ShapeUtil::ChangeElementType(in, C64);
   1789       result.set_dimensions(result.dimensions_size() - 1,
   1790                             fft_length[fft_rank - 1] / 2 + 1);
   1791       return result;
   1792     }
   1793     case IRFFT: {
   1794       if (in.element_type() != C64) {
   1795         return InvalidArgument("IRFFT requires C64 input type, found %s",
   1796                                PrimitiveType_Name(in.element_type()).c_str());
   1797       }
   1798       RET_CHECK_RANK(in);
   1799       Shape result = ShapeUtil::ComplexComponentShape(in);
   1800       for (int i = 0; i < fft_rank - 1; i++) {
   1801         if (in.dimensions(in.dimensions_size() - fft_rank + i) !=
   1802             fft_length[i]) {
   1803           return InvalidArgument(
   1804               "IRFFT requires all but one innermost dimensions match "
   1805               "fft_length, but dimension %lld is %lld and should be %lld",
   1806               in.dimensions_size() - fft_rank + i,
   1807               in.dimensions(in.dimensions_size() - fft_rank + i),
   1808               fft_length[i]);
   1809         }
   1810       }
   1811       if (in.dimensions(in.dimensions_size() - 1) !=
   1812           fft_length[fft_rank - 1] / 2 + 1) {
   1813         return InvalidArgument(
   1814             "IRFFT requires innermost dimension matches fft_length/2+1, but "
   1815             "dimension %d is %lld and should be %lld",
   1816             in.dimensions_size() - 1, in.dimensions(in.dimensions_size() - 1),
   1817             fft_length[fft_rank - 1] / 2 + 1);
   1818       }
   1819       result.set_dimensions(result.dimensions_size() - 1,
   1820                             fft_length[fft_rank - 1]);
   1821       return result;
   1822     }
   1823     default:
   1824       LOG(FATAL) << "Unexpected fft_type: " << fft_type;
   1825   }
   1826 #undef RET_CHECK_RANK
   1827 }
   1828 
   1829 /* static */ StatusOr<Shape> ShapeInference::InferCrossReplicaSumShape(
   1830     tensorflow::gtl::ArraySlice<const Shape*> operand_shapes) {
   1831   for (const Shape* operand_shape : operand_shapes) {
   1832     TF_RETURN_IF_ERROR(
   1833         ExpectNotTupleOrOpaque(*operand_shape, "operand of cross replica sum"));
   1834   }
   1835   if (operand_shapes.size() == 1) {
   1836     return *operand_shapes[0];
   1837   }
   1838   std::vector<Shape> operand_shape_values;
   1839   for (const Shape* operand_shape : operand_shapes) {
   1840     operand_shape_values.push_back(*operand_shape);
   1841   }
   1842   return ShapeUtil::MakeTupleShape(operand_shape_values);
   1843 }
   1844 
   1845 /* static */ StatusOr<Shape> ShapeInference::InferReduceShape(
   1846     const Shape& arg, const Shape& init_value,
   1847     tensorflow::gtl::ArraySlice<int64> dimensions_to_reduce,
   1848     const ProgramShape& to_apply) {
   1849   // Check that the dimension to reduce are in-bounds for the given shape.
   1850   for (int64 dimension : dimensions_to_reduce) {
   1851     if (dimension >= ShapeUtil::Rank(arg) || dimension < 0) {
   1852       return InvalidArgument(
   1853           "attempting to reduce out-of-bounds dimension %lld in shape %s",
   1854           dimension, ShapeUtil::HumanString(arg).c_str());
   1855     }
   1856   }
   1857   TF_RETURN_IF_ERROR(
   1858       VerifyReducerShape(to_apply, init_value, arg.element_type()));
   1859 
   1860   std::set<int64> dimensions_to_reduce_set(dimensions_to_reduce.begin(),
   1861                                            dimensions_to_reduce.end());
   1862   std::vector<int64> new_dimensions;
   1863   for (int i = 0; i < ShapeUtil::Rank(arg); ++i) {
   1864     if (dimensions_to_reduce_set.find(i) == dimensions_to_reduce_set.end()) {
   1865       new_dimensions.push_back(arg.dimensions(i));
   1866     }
   1867   }
   1868 
   1869   return ShapeUtil::MakeShape(to_apply.result().element_type(), new_dimensions);
   1870 }
   1871 
   1872 /* static */ StatusOr<Shape> ShapeInference::InferReduceWindowShape(
   1873     const Shape& operand_shape, const Shape& init_value_shape,
   1874     const Window& window, const ProgramShape& to_apply_shape) {
   1875   TF_RETURN_IF_ERROR(
   1876       ExpectNotTupleOrOpaque(operand_shape, "operand of reduce-window"));
   1877   TF_RETURN_IF_ERROR(VerifyReducerShape(to_apply_shape, init_value_shape,
   1878                                         operand_shape.element_type()));
   1879   return InferWindowOutputShape(operand_shape, window,
   1880                                 init_value_shape.element_type(),
   1881                                 /*allow_negative_padding=*/false);
   1882 }
   1883 
   1884 /* static */ StatusOr<Shape> ShapeInference::InferSelectAndScatterShape(
   1885     const Shape& operand_shape, const ProgramShape& select_shape,
   1886     const Window& window, const Shape& source_shape,
   1887     const Shape& init_value_shape, const ProgramShape& scatter_shape) {
   1888   TF_RETURN_IF_ERROR(
   1889       ExpectNotTupleOrOpaque(operand_shape, "operand of select-and-scatter"));
   1890 
   1891   // Check if the select function has a proper shape of (T,T) -> PRED.
   1892   if (select_shape.parameters_size() != 2) {
   1893     return InvalidArgument(
   1894         "select function must take 2 parameters, but "
   1895         "takes %d parameter(s).",
   1896         select_shape.parameters_size());
   1897   }
   1898   const Shape& select_result_shape = select_shape.result();
   1899   if (!ShapeUtil::Compatible(select_result_shape,
   1900                              ShapeUtil::MakeShape(PRED, {}))) {
   1901     return Unimplemented("select function must have rank-0 PRED result.");
   1902   }
   1903   const Shape& operand_element_shape =
   1904       ShapeUtil::MakeShape(operand_shape.element_type(), {});
   1905   if (!ShapeUtil::CompatibleIgnoringFpPrecision(operand_element_shape,
   1906                                                 select_shape.parameters(0))) {
   1907     return InvalidArgument(
   1908         "select function's first parameter shape currently must "
   1909         "match the operand element shape. Got %s vs %s",
   1910         ShapeUtil::HumanString(select_shape.parameters(0)).c_str(),
   1911         ShapeUtil::HumanString(operand_element_shape).c_str());
   1912   }
   1913   if (!ShapeUtil::CompatibleIgnoringFpPrecision(operand_element_shape,
   1914                                                 select_shape.parameters(1))) {
   1915     return InvalidArgument(
   1916         "select function's second parameter shape currently must "
   1917         "match the operand element shape. Got %s vs %s",
   1918         ShapeUtil::HumanString(select_shape.parameters(1)).c_str(),
   1919         ShapeUtil::HumanString(operand_element_shape).c_str());
   1920   }
   1921 
   1922   // Check if the scatter function has a proper shape as a reduction.
   1923   TF_RETURN_IF_ERROR(VerifyReducerShape(scatter_shape, init_value_shape,
   1924                                         source_shape.element_type()));
   1925 
   1926   // Check if the result shape of window operation matches the source shape.
   1927   TF_ASSIGN_OR_RETURN(const Shape& window_result_shape,
   1928                       InferWindowOutputShape(operand_shape, window,
   1929                                              operand_shape.element_type(),
   1930                                              /*allow_negative_padding=*/false));
   1931   if (!ShapeUtil::CompatibleIgnoringFpPrecision(source_shape,
   1932                                                 window_result_shape)) {
   1933     return InvalidArgument(
   1934         "source shape does not match the shape of window-reduced operand: "
   1935         "source(%s), window-reduced operand(%s)",
   1936         ShapeUtil::HumanString(source_shape).c_str(),
   1937         ShapeUtil::HumanString(window_result_shape).c_str());
   1938   }
   1939   return operand_shape;
   1940 }
   1941 
   1942 /* static */ StatusOr<Shape> ShapeInference::InferSliceShape(
   1943     const Shape& arg, tensorflow::gtl::ArraySlice<int64> starts,
   1944     tensorflow::gtl::ArraySlice<int64> limits,
   1945     tensorflow::gtl::ArraySlice<int64> strides) {
   1946   auto error = [&](const string& message) {
   1947     return InvalidArgument(
   1948         "%s in slice operation; argument shape: %s; starts: {%s}; limits: "
   1949         "{%s}; strides: {%s}",
   1950         message.c_str(), ShapeUtil::HumanString(arg).c_str(),
   1951         Join(starts, ",").c_str(), Join(limits, ",").c_str(),
   1952         Join(strides, ",").c_str());
   1953   };
   1954   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(arg, "operand of slice"));
   1955   VLOG(2) << tensorflow::strings::Printf(
   1956       "slicing shape %s starts={%s} limits={%s}",
   1957       ShapeUtil::HumanString(arg).c_str(), Join(starts, ", ").c_str(),
   1958       Join(limits, ", ").c_str());
   1959 
   1960   if (starts.size() != limits.size()) {
   1961     return error(Printf("slice start and limit sizes differ: %zu vs %zu",
   1962                         starts.size(), limits.size()));
   1963   }
   1964 
   1965   if (starts.size() != strides.size()) {
   1966     return error(Printf("slice start and strides sizes differ: %zu vs %zu",
   1967                         starts.size(), strides.size()));
   1968   }
   1969 
   1970   if (starts.size() != ShapeUtil::Rank(arg)) {
   1971     return InvalidArgument(
   1972         "slice index count does not match argument rank: %zu vs %lld",
   1973         starts.size(), ShapeUtil::Rank(arg));
   1974   }
   1975 
   1976   std::vector<int64> sizes;
   1977   for (int64 dimension = 0; dimension < starts.size(); ++dimension) {
   1978     int64 start_index = starts[dimension];
   1979     int64 limit_index = limits[dimension];
   1980     int64 stride = strides[dimension];
   1981     if (start_index < 0) {
   1982       return InvalidArgument("negative start index to slice: %lld",
   1983                              start_index);
   1984     }
   1985     if (limit_index > arg.dimensions(dimension)) {
   1986       return error(
   1987           Printf("limit index (%lld) must be less than or equal to dimension "
   1988                  "size (%lld)",
   1989                  limit_index, arg.dimensions(dimension)));
   1990     }
   1991     VLOG(2) << tensorflow::strings::Printf("starts[%lld] = %lld", dimension,
   1992                                            start_index);
   1993     VLOG(2) << tensorflow::strings::Printf("limits[%lld] = %lld", dimension,
   1994                                            limit_index);
   1995     if (start_index > limit_index) {
   1996       return error(
   1997           Printf("limit index (%lld) must be greater or equal to "
   1998                  "start index (%lld) in slice with positive stride",
   1999                  limit_index, start_index));
   2000     }
   2001     if (stride <= 0) {
   2002       return InvalidArgument("stride (%lld) must be positive", stride);
   2003     }
   2004     sizes.push_back((limit_index - start_index + stride - 1) / stride);
   2005   }
   2006 
   2007   return ShapeUtil::MakeShape(arg.element_type(), sizes);
   2008 }
   2009 
   2010 /* static */ StatusOr<Shape> ShapeInference::InferDynamicSliceShape(
   2011     const Shape& operand_shape, const Shape& start_indices_shape,
   2012     tensorflow::gtl::ArraySlice<int64> slice_sizes) {
   2013   TF_RETURN_IF_ERROR(
   2014       ExpectNotTupleOrOpaque(operand_shape, "operand of dynamic slice"));
   2015   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(start_indices_shape,
   2016                                             "start indices of dynamic slice"));
   2017 
   2018   VLOG(2) << tensorflow::strings::Printf(
   2019       "slicing shape %s at dynamic start_indices %s with slice_sizes={%s}",
   2020       ShapeUtil::HumanString(operand_shape).c_str(),
   2021       ShapeUtil::HumanString(start_indices_shape).c_str(),
   2022       Join(slice_sizes, ", ").c_str());
   2023 
   2024   if (ShapeUtil::Rank(start_indices_shape) != 1) {
   2025     return InvalidArgument(
   2026         "dynamic slice start indices of rank %lld must be rank1.",
   2027         ShapeUtil::Rank(start_indices_shape));
   2028   }
   2029 
   2030   if (!ShapeUtil::ElementIsIntegral(start_indices_shape)) {
   2031     return InvalidArgument(
   2032         "dynamic slice start indices must be of integral type.");
   2033   }
   2034 
   2035   const int64 start_num_dims = start_indices_shape.dimensions(0);
   2036   if (ShapeUtil::Rank(operand_shape) != start_num_dims) {
   2037     return InvalidArgument(
   2038         "dynamic slice start number of dimensions %lld (%s) must match rank "
   2039         "%lld of slice input (%s)",
   2040         start_num_dims, ShapeUtil::HumanString(start_indices_shape).c_str(),
   2041         ShapeUtil::Rank(operand_shape),
   2042         ShapeUtil::HumanString(operand_shape).c_str());
   2043   }
   2044 
   2045   if (slice_sizes.size() != ShapeUtil::Rank(operand_shape)) {
   2046     return InvalidArgument(
   2047         "dynamic slice index count does not match argument rank: %zu vs %lld",
   2048         slice_sizes.size(), ShapeUtil::Rank(operand_shape));
   2049   }
   2050 
   2051   for (int64 dim = 0; dim < slice_sizes.size(); ++dim) {
   2052     const int64 input_dim_size = operand_shape.dimensions(dim);
   2053     const int64 slice_dim_size = slice_sizes[dim];
   2054     if (slice_dim_size < 0) {
   2055       return InvalidArgument("negative size index to dynamic slice: %lld",
   2056                              slice_dim_size);
   2057     }
   2058     if (slice_dim_size > input_dim_size) {
   2059       return InvalidArgument(
   2060           "slice dim size %lld greater than dynamic slice dimension: %lld",
   2061           slice_dim_size, input_dim_size);
   2062     }
   2063     VLOG(2) << tensorflow::strings::Printf("slice_sizes[%lld] = %lld", dim,
   2064                                            slice_dim_size);
   2065   }
   2066 
   2067   return ShapeUtil::MakeShape(operand_shape.element_type(), slice_sizes);
   2068 }
   2069 
   2070 /* static */ StatusOr<Shape> ShapeInference::InferDynamicUpdateSliceShape(
   2071     const Shape& operand_shape, const Shape& update_shape,
   2072     const Shape& start_indices_shape) {
   2073   TF_RETURN_IF_ERROR(
   2074       ExpectNotTupleOrOpaque(operand_shape, "operand of dynamic update slice"));
   2075   TF_RETURN_IF_ERROR(
   2076       ExpectNotTupleOrOpaque(update_shape, "update of dynamic update slice"));
   2077   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(
   2078       start_indices_shape, "start indices of dynamic update slice"));
   2079 
   2080   VLOG(2) << tensorflow::strings::Printf(
   2081       "updating slice of shape %s at dynamic start_indices %s with update "
   2082       "shape %s",
   2083       ShapeUtil::HumanString(operand_shape).c_str(),
   2084       ShapeUtil::HumanString(start_indices_shape).c_str(),
   2085       ShapeUtil::HumanString(update_shape).c_str());
   2086 
   2087   if (ShapeUtil::Rank(start_indices_shape) != 1) {
   2088     return InvalidArgument(
   2089         "dynamic update slice start indices of rank %lld must be rank1.",
   2090         ShapeUtil::Rank(start_indices_shape));
   2091   }
   2092 
   2093   if (!ShapeUtil::ElementIsIntegral(start_indices_shape)) {
   2094     return InvalidArgument(
   2095         "dynamic update slice start indices must be of integral type.");
   2096   }
   2097 
   2098   const int64 start_num_dims = start_indices_shape.dimensions(0);
   2099   if (ShapeUtil::Rank(operand_shape) != start_num_dims) {
   2100     return InvalidArgument(
   2101         "dynamic slice start number of dimensions %lld (%s) must match rank "
   2102         "%lld of slice input (%s)",
   2103         start_num_dims, ShapeUtil::HumanString(start_indices_shape).c_str(),
   2104         ShapeUtil::Rank(operand_shape),
   2105         ShapeUtil::HumanString(operand_shape).c_str());
   2106   }
   2107 
   2108   if (ShapeUtil::Rank(update_shape) != ShapeUtil::Rank(operand_shape)) {
   2109     return InvalidArgument(
   2110         "dynamic update slice update rank does not match argument rank: "
   2111         "%lld vs %lld",
   2112         ShapeUtil::Rank(update_shape), ShapeUtil::Rank(operand_shape));
   2113   }
   2114 
   2115   if (!ShapeUtil::SameElementTypeIgnoringFpPrecision(operand_shape,
   2116                                                      update_shape)) {
   2117     return InvalidArgument(
   2118         "dynamic update slice update element type does not match argument. "
   2119         "operand.element_type: %s vs update.element_type: %s",
   2120         PrimitiveType_Name(operand_shape.element_type()).c_str(),
   2121         PrimitiveType_Name(update_shape.element_type()).c_str());
   2122   }
   2123 
   2124   for (int64 dim = 0; dim < ShapeUtil::Rank(operand_shape); ++dim) {
   2125     const int64 input_dim_size = operand_shape.dimensions(dim);
   2126     const int64 update_dim_size = update_shape.dimensions(dim);
   2127     if (update_dim_size < 0) {
   2128       return InvalidArgument(
   2129           "size index %lld to dynamic update slice must be >= 0",
   2130           update_dim_size);
   2131     }
   2132     if (update_dim_size > input_dim_size) {
   2133       return InvalidArgument(
   2134           "update dim size %lld greater than dynamic slice dimension: %lld",
   2135           update_dim_size, input_dim_size);
   2136     }
   2137     VLOG(2) << tensorflow::strings::Printf("update_sizes[%lld] = %lld", dim,
   2138                                            update_dim_size);
   2139   }
   2140 
   2141   return operand_shape;
   2142 }
   2143 
   2144 /*static */ StatusOr<Shape> ShapeInference::InferReverseShape(
   2145     const Shape& operand_shape, tensorflow::gtl::ArraySlice<int64> dimensions) {
   2146   TF_RETURN_IF_ERROR(
   2147       ExpectNotTupleOrOpaque(operand_shape, "operand of reverse"));
   2148   if (!AllUnique(dimensions)) {
   2149     return InvalidArgument("a dimension number is duplicated in reverse");
   2150   }
   2151   for (int64 dimension : dimensions) {
   2152     if (dimension >= ShapeUtil::Rank(operand_shape) || dimension < 0) {
   2153       return InvalidArgument(
   2154           "one of the reverse dimensions (%lld) is out-of-bounds in shape %s",
   2155           dimension, ShapeUtil::HumanString(operand_shape).c_str());
   2156     }
   2157   }
   2158   return operand_shape;
   2159 }
   2160 
   2161 /* static */ StatusOr<Shape> ShapeInference::InferGetTupleElementShape(
   2162     const Shape& arg, int64 index) {
   2163   if (!ShapeUtil::IsTuple(arg)) {
   2164     return InvalidArgument(
   2165         "cannot infer shape: attempting to index into non-tuple: %s",
   2166         ShapeUtil::HumanString(arg).c_str());
   2167   }
   2168 
   2169   if (index >= arg.tuple_shapes_size()) {
   2170     return InvalidArgument(
   2171         "cannot infer shape: attempt to index out of tuple bounds: %lld "
   2172         ">= %d in shape %s",
   2173         index, arg.tuple_shapes_size(), ShapeUtil::HumanString(arg).c_str());
   2174   }
   2175 
   2176   return arg.tuple_shapes(index);
   2177 }
   2178 
   2179 /* static */ StatusOr<Shape> ShapeInference::InferWhileShape(
   2180     const ProgramShape& condition, const ProgramShape& body,
   2181     const Shape& init) {
   2182   // Check the number of parameters for given computations.
   2183   if (condition.parameters_size() != 1) {
   2184     return InvalidArgument("condition must take 1 arguments; got %d",
   2185                            condition.parameters_size());
   2186   }
   2187   if (body.parameters_size() != 1) {
   2188     return InvalidArgument("body must take 1 arguments; got %d",
   2189                            body.parameters_size());
   2190   }
   2191 
   2192   auto shape_string = [&]() {
   2193     return tensorflow::strings::Printf(
   2194         "condition: %s; body: %s; init: %s",
   2195         ShapeUtil::HumanString(condition).c_str(),
   2196         ShapeUtil::HumanString(body).c_str(),
   2197         ShapeUtil::HumanString(init).c_str());
   2198   };
   2199 
   2200   // Check the shapes of computation parameters and return types.
   2201   if (!ShapeUtil::ShapeIs(condition.result(), PRED, {})) {
   2202     return InvalidArgument("condition must return a boolean; got %s",
   2203                            shape_string().c_str());
   2204   }
   2205   if (!ShapeUtil::Compatible(body.result(), condition.parameters(0)) ||
   2206       !ShapeUtil::Compatible(body.result(), body.parameters(0)) ||
   2207       !ShapeUtil::Compatible(body.result(), init)) {
   2208     return InvalidArgument(
   2209         "the parameter of condition and body, the result of the body, and init "
   2210         "must all have the same shape; got %s",
   2211         shape_string().c_str());
   2212   }
   2213 
   2214   return init;
   2215 }
   2216 
   2217 /* static */ StatusOr<Shape> ShapeInference::InferConditionalShape(
   2218     const Shape& predicate, const Shape& true_operand,
   2219     const Shape& false_operand, const ProgramShape& true_computation,
   2220     const ProgramShape& false_computation) {
   2221   if (!ShapeUtil::ShapeIs(predicate, PRED, {})) {
   2222     return InvalidArgument("predicate must be a boolean; got %s.",
   2223                            ShapeUtil::HumanString(predicate).c_str());
   2224   }
   2225 
   2226   if (true_computation.parameters_size() != 1) {
   2227     return InvalidArgument("true_computation must take 1 argument; got %d.",
   2228                            true_computation.parameters_size());
   2229   }
   2230   if (!ShapeUtil::Compatible(true_computation.parameters(0), true_operand)) {
   2231     auto true_shape_string = [&]() {
   2232       return tensorflow::strings::Printf(
   2233           "true_operand: %s; true_computation: %s",
   2234           ShapeUtil::HumanString(true_operand).c_str(),
   2235           ShapeUtil::HumanString(true_computation).c_str());
   2236     };
   2237     return InvalidArgument(
   2238         "true_operand must match the shape of the only parameter of "
   2239         "true_computation: got %s.",
   2240         true_shape_string().c_str());
   2241   }
   2242 
   2243   if (false_computation.parameters_size() != 1) {
   2244     return InvalidArgument("false_computation must take 1 argument; got %d.",
   2245                            false_computation.parameters_size());
   2246   }
   2247   if (!ShapeUtil::Compatible(false_computation.parameters(0), false_operand)) {
   2248     auto false_shape_string = [&]() {
   2249       return tensorflow::strings::Printf(
   2250           "false_operand: %s; false_computation: %s",
   2251           ShapeUtil::HumanString(false_operand).c_str(),
   2252           ShapeUtil::HumanString(false_computation).c_str());
   2253     };
   2254     return InvalidArgument(
   2255         "false_operand must match the shape of the only parameter of "
   2256         "false_computation: got %s.",
   2257         false_shape_string().c_str());
   2258   }
   2259   if (!ShapeUtil::Compatible(true_computation.result(),
   2260                              false_computation.result())) {
   2261     auto shape_string = [&]() {
   2262       return tensorflow::strings::Printf(
   2263           "true_computation result: %s; false_computation result: %s.",
   2264           ShapeUtil::HumanString(true_computation.result()).c_str(),
   2265           ShapeUtil::HumanString(false_computation.result()).c_str());
   2266     };
   2267     return InvalidArgument(
   2268         "the result of true_computation and false_computation must have the "
   2269         "same shape: got %s.",
   2270         shape_string().c_str());
   2271   }
   2272   return true_computation.result();
   2273 }
   2274 
   2275 /* static */ StatusOr<Shape> ShapeInference::InferBroadcastShape(
   2276     const Shape& operand, tensorflow::gtl::ArraySlice<int64> broadcast_sizes) {
   2277   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(operand, "operand of broadcast"));
   2278   for (int64 size : broadcast_sizes) {
   2279     if (size < 0) {
   2280       return InvalidArgument("Broadcast with negative dimension size %lld.",
   2281                              size);
   2282     }
   2283   }
   2284 
   2285   std::vector<int64> dimensions(operand.dimensions_size() +
   2286                                 broadcast_sizes.size());
   2287   std::copy(broadcast_sizes.begin(), broadcast_sizes.end(), dimensions.begin());
   2288   std::copy(operand.dimensions().begin(), operand.dimensions().end(),
   2289             dimensions.begin() + broadcast_sizes.size());
   2290   return ShapeUtil::MakeShape(operand.element_type(), dimensions);
   2291 }
   2292 
   2293 /* static */ StatusOr<Shape> ShapeInference::InferReshapeShape(
   2294     const Shape& operand, tensorflow::gtl::ArraySlice<int64> dimensions,
   2295     tensorflow::gtl::ArraySlice<int64> new_sizes) {
   2296   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(operand, "reshape"));
   2297 
   2298   Shape inferred_shape =
   2299       ShapeUtil::MakeShape(operand.element_type(), new_sizes);
   2300   VLOG(3) << "Reshape inferred shape: "
   2301           << ShapeUtil::HumanString(inferred_shape);
   2302 
   2303   if (ShapeUtil::ElementsIn(operand) != ShapeUtil::ElementsIn(inferred_shape)) {
   2304     return InvalidArgument(
   2305         "reshape operation has mismatched element counts: from=%lld (%s) "
   2306         "to=%lld (%s)",
   2307         ShapeUtil::ElementsIn(operand), ShapeUtil::HumanString(operand).c_str(),
   2308         ShapeUtil::ElementsIn(inferred_shape),
   2309         ShapeUtil::HumanString(inferred_shape).c_str());
   2310   }
   2311 
   2312   std::vector<int64> indices(ShapeUtil::Rank(operand));
   2313   std::iota(indices.begin(), indices.end(), 0);
   2314   if (dimensions.size() != ShapeUtil::Rank(operand) ||
   2315       !std::is_permutation(dimensions.begin(), dimensions.end(),
   2316                            indices.begin())) {
   2317     return InvalidArgument(
   2318         "Reshape dimensions [%s] are not a permutation of the operand "
   2319         "dimensions (operand shape is %s).",
   2320         Join(dimensions, ",").c_str(), ShapeUtil::HumanString(operand).c_str());
   2321   }
   2322 
   2323   return inferred_shape;
   2324 }
   2325 
   2326 /* static */ StatusOr<Shape> ShapeInference::InferTransposeShape(
   2327     const Shape& operand, tensorflow::gtl::ArraySlice<int64> dimensions) {
   2328   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(operand, "transpose"));
   2329 
   2330   std::vector<int64> indices(ShapeUtil::Rank(operand));
   2331   std::iota(indices.begin(), indices.end(), 0);
   2332   if (dimensions.size() != ShapeUtil::Rank(operand) ||
   2333       !std::is_permutation(dimensions.begin(), dimensions.end(),
   2334                            indices.begin())) {
   2335     return InvalidArgument(
   2336         "Transpose dimensions not a permutation of the operand dimensions.");
   2337   }
   2338 
   2339   // Permute(dimensions,input) computes output[dimensions[i]]=input[i]. However,
   2340   // we need output[i]=input[dimensions[i]] which is
   2341   // Permute(Inverse(dimensions),input).
   2342   return ShapeUtil::PermuteDimensions(InversePermutation(dimensions), operand);
   2343 }
   2344 
   2345 // TODO(b/36794510): Make broadcast semantics more consistent, by supporting
   2346 // "degenerate" cases, as with binary elementwise ops.
   2347 /* static */ StatusOr<Shape> ShapeInference::InferClampShape(
   2348     const Shape& min, const Shape& operand, const Shape& max) {
   2349   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(min, "clamp min"));
   2350   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(operand, "clamp operand"));
   2351   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(max, "clamp max"));
   2352   if (!ShapeUtil::SameElementTypeIgnoringFpPrecision(min, operand) ||
   2353       !ShapeUtil::SameElementTypeIgnoringFpPrecision(max, operand)) {
   2354     return InvalidArgument("clamp op with different operand types: %s, %s, %s",
   2355                            ShapeUtil::HumanString(min).c_str(),
   2356                            ShapeUtil::HumanString(operand).c_str(),
   2357                            ShapeUtil::HumanString(max).c_str());
   2358   }
   2359   if (((ShapeUtil::CompatibleIgnoringFpPrecision(min, operand) ||
   2360         ShapeUtil::IsScalar(min)) &&
   2361        (ShapeUtil::CompatibleIgnoringFpPrecision(max, operand) ||
   2362         ShapeUtil::IsScalar(max)))) {
   2363     return operand;
   2364   }
   2365   if (ShapeUtil::IsScalar(operand)) {
   2366     if (ShapeUtil::CompatibleIgnoringFpPrecision(min, max)) {
   2367       return ShapeUtil::ChangeElementType(min, operand.element_type());
   2368     } else if (ShapeUtil::IsScalar(min)) {
   2369       return ShapeUtil::ChangeElementType(max, operand.element_type());
   2370     } else if (ShapeUtil::IsScalar(max)) {
   2371       return ShapeUtil::ChangeElementType(min, operand.element_type());
   2372     }
   2373   }
   2374   return Unimplemented(
   2375       "not yet implemented: %s, %s <clamp> %s", min.ShortDebugString().c_str(),
   2376       max.ShortDebugString().c_str(), operand.ShortDebugString().c_str());
   2377 }
   2378 
   2379 // TODO(b/36794510): Make broadcast semantics more consistent, by supporting
   2380 // "degenerate" cases, as with binary elementwise ops, as well as scalar
   2381 // broadcast from all operands, not just the predicate.
   2382 /* static */ StatusOr<Shape> ShapeInference::InferSelectShape(
   2383     const Shape& pred, const Shape& on_true, const Shape& on_false) {
   2384   bool compatible;
   2385   if (ShapeUtil::IsTuple(on_true)) {
   2386     // Select only defines the top-level buffer, so if it's a tuple, the two
   2387     // input must match exactly.
   2388     compatible = ShapeUtil::Compatible(on_true, on_false);
   2389   } else {
   2390     compatible = ShapeUtil::CompatibleIgnoringFpPrecision(on_true, on_false);
   2391   }
   2392   if (!compatible) {
   2393     return InvalidArgument(
   2394         "operands to select must be the same shape; got %s and %s",
   2395         ShapeUtil::HumanString(on_true).c_str(),
   2396         ShapeUtil::HumanString(on_false).c_str());
   2397   }
   2398   if (pred.element_type() != PRED) {
   2399     return InvalidArgument(
   2400         "select's pred operand must have PRED element type; got %s",
   2401         ShapeUtil::HumanString(pred).c_str());
   2402   }
   2403   if (ShapeUtil::SameDimensions(pred, on_true) || ShapeUtil::Rank(pred) == 0) {
   2404     // By this stage we know that pred's element type is PRED. Therefore, this
   2405     // check restricts pred to be a PRED scalar, or a PRED array with the same
   2406     // dimensions as on_true and on_false.
   2407     return ShapeUtil::ChangeElementType(
   2408         on_true, ShapeUtil::HigherPrecisionElementType(on_true, on_false));
   2409   } else {
   2410     return Unimplemented(
   2411         "select operation with non-scalar predicate with dimensionality "
   2412         " different from the other operands: %s",
   2413         ShapeUtil::HumanString(pred).c_str());
   2414   }
   2415 }
   2416 
   2417 /* static */ StatusOr<Shape> ShapeInference::InferCallShape(
   2418     tensorflow::gtl::ArraySlice<const Shape*> arg_shapes,
   2419     const ProgramShape& to_apply) {
   2420   // The applied function's arity equals the number of arguments.
   2421   if (arg_shapes.size() != to_apply.parameters_size()) {
   2422     string computation_signature = ShapeUtil::HumanString(to_apply);
   2423     string argument_shapes =
   2424         Join(arg_shapes, ", ", [](string* out, const Shape* shape) {
   2425           tensorflow::strings::StrAppend(out, ShapeUtil::HumanString(*shape));
   2426         });
   2427     return InvalidArgument(
   2428         "Call applied function arity must match number of arguments; got: "
   2429         "arity: %d, arguments: %zu; computation signature: %s; argument "
   2430         "shapes: [%s]",
   2431         to_apply.parameters_size(), arg_shapes.size(),
   2432         computation_signature.c_str(), argument_shapes.c_str());
   2433   }
   2434 
   2435   // All arguments must be compatible with the program shape.
   2436   for (int i = 0; i < arg_shapes.size(); ++i) {
   2437     const Shape& arg_shape = *arg_shapes[i];
   2438     const Shape& param_shape = to_apply.parameters(i);
   2439     if (!ShapeUtil::Compatible(arg_shape, param_shape)) {
   2440       return InvalidArgument(
   2441           "Call parameter must match argument; got parameter %d shape: %s, "
   2442           "argument shape: %s",
   2443           i, ShapeUtil::HumanString(param_shape).c_str(),
   2444           ShapeUtil::HumanString(arg_shape).c_str());
   2445     }
   2446   }
   2447 
   2448   return to_apply.result();
   2449 }
   2450 
   2451 static Status ValidateGatherDimensionNumbers(
   2452     const Shape& input_shape,
   2453     tensorflow::gtl::ArraySlice<int64> gather_indices_shape,
   2454     const GatherDimensionNumbers& dim_numbers) {
   2455   if (!c_is_sorted(dim_numbers.output_window_dims())) {
   2456     return InvalidArgument(
   2457         "Output window dimensions in gather op must be ascending; got: %s",
   2458         Join(dim_numbers.output_window_dims(), ", ").c_str());
   2459   }
   2460 
   2461   if (c_adjacent_find(dim_numbers.output_window_dims()) !=
   2462       dim_numbers.output_window_dims().end()) {
   2463     return InvalidArgument(
   2464         "Output window dimensions in gather op must not repeat; got: %s",
   2465         Join(dim_numbers.output_window_dims(), ", ").c_str());
   2466   }
   2467 
   2468   const int64 output_window_dim_count = dim_numbers.output_window_dims_size();
   2469   const int64 output_shape_rank =
   2470       output_window_dim_count + gather_indices_shape.size();
   2471 
   2472   for (int i = 0; i < dim_numbers.output_window_dims_size(); ++i) {
   2473     int64 window_index = dim_numbers.output_window_dims(i);
   2474     if (window_index < 0 || window_index >= output_shape_rank) {
   2475       return InvalidArgument(
   2476           "Window index %d in gather op is out of bounds; got %lld, but should "
   2477           "have been in"
   2478           "[0,%lld)",
   2479           i, window_index, output_shape_rank);
   2480     }
   2481   }
   2482 
   2483   if (dim_numbers.gather_dims_to_operand_dims_size() !=
   2484       gather_indices_shape.back()) {
   2485     return InvalidArgument(
   2486         "There must be exactly as many elements in gather_dims_to_operand_dims "
   2487         "as there are elements in the last dimension of %%gather_indices; got: "
   2488         "%d, expected %lld",
   2489         dim_numbers.gather_dims_to_operand_dims_size(),
   2490         gather_indices_shape.back());
   2491   }
   2492 
   2493   for (int i = 0; i < dim_numbers.gather_dims_to_operand_dims_size(); i++) {
   2494     int64 gather_dim_to_input_dim = dim_numbers.gather_dims_to_operand_dims(i);
   2495     if (gather_dim_to_input_dim < 0 ||
   2496         gather_dim_to_input_dim >= input_shape.dimensions_size()) {
   2497       return InvalidArgument(
   2498           "Invalid gather_dims_to_operand_dims mapping; domain is [0, %d), "
   2499           "got: %d->%lld",
   2500           input_shape.dimensions_size(), i, gather_dim_to_input_dim);
   2501     }
   2502   }
   2503 
   2504   std::vector<int64> sorted_gather_dims_to_operand_dims(
   2505       dim_numbers.gather_dims_to_operand_dims().begin(),
   2506       dim_numbers.gather_dims_to_operand_dims().end());
   2507 
   2508   c_sort(sorted_gather_dims_to_operand_dims);
   2509 
   2510   if (c_adjacent_find(sorted_gather_dims_to_operand_dims) !=
   2511       sorted_gather_dims_to_operand_dims.end()) {
   2512     return InvalidArgument(
   2513         "Repeated dimensions are not allowed in gather_dims_to_operand_dims; "
   2514         "got: %s",
   2515         Join(dim_numbers.gather_dims_to_operand_dims(), ", ").c_str());
   2516   }
   2517 
   2518   for (int64 elided_dim : dim_numbers.elided_window_dims()) {
   2519     if (elided_dim < 0 || elided_dim >= input_shape.dimensions_size()) {
   2520       return InvalidArgument(
   2521           "Invalid elided_window_dims set in gather op; valid range is [0, "
   2522           "%d), got: %lld",
   2523           input_shape.dimensions_size(), elided_dim);
   2524     }
   2525   }
   2526 
   2527   if (!c_is_sorted(dim_numbers.elided_window_dims())) {
   2528     return InvalidArgument(
   2529         "elided_window_dims in gather op must be sorted; got: %s",
   2530         Join(dim_numbers.elided_window_dims(), ", ").c_str());
   2531   }
   2532 
   2533   if (c_adjacent_find(dim_numbers.elided_window_dims()) !=
   2534       dim_numbers.elided_window_dims().end()) {
   2535     return InvalidArgument(
   2536         "Repeated dimensions not allowed in elided_window_dims in gather op; "
   2537         "got: %s",
   2538         Join(dim_numbers.elided_window_dims(), ", ").c_str());
   2539   }
   2540 
   2541   return Status::OK();
   2542 }
   2543 
   2544 /*static*/ StatusOr<Shape> ShapeInference::InferGatherShape(
   2545     const Shape& input_shape, const Shape& gather_indices_shape,
   2546     const GatherDimensionNumbers& gather_dim_numbers,
   2547     tensorflow::gtl::ArraySlice<int64> window_bounds) {
   2548   TF_RETURN_IF_ERROR(
   2549       ExpectNotTupleOrOpaque(input_shape, "input tensor operand gather op"));
   2550   TF_RETURN_IF_ERROR(ExpectNotTupleOrOpaque(
   2551       gather_indices_shape, "gather indices operand of gather op"));
   2552 
   2553   if (gather_indices_shape.dimensions_size() < 1) {
   2554     return InvalidArgument(
   2555         "Gather indices parameter must at least of rank 1; got %s",
   2556         ShapeUtil::HumanString(gather_indices_shape).c_str());
   2557   }
   2558 
   2559   if (!ShapeUtil::ElementIsIntegral(gather_indices_shape)) {
   2560     return InvalidArgument(
   2561         "Gather indices parameter must be an integral tensor; got %s",
   2562         ShapeUtil::HumanString(gather_indices_shape).c_str());
   2563   }
   2564 
   2565   std::vector<int64> expanded_gather_indices_shape;
   2566   // We implicitly reshape gather indices of shape P[N] to P[N,1].
   2567   expanded_gather_indices_shape.reserve(gather_indices_shape.dimensions_size());
   2568   c_copy(gather_indices_shape.dimensions(),
   2569          std::back_inserter(expanded_gather_indices_shape));
   2570   if (expanded_gather_indices_shape.size() == 1) {
   2571     expanded_gather_indices_shape.push_back(1);
   2572   }
   2573 
   2574   TF_RETURN_IF_ERROR(ValidateGatherDimensionNumbers(
   2575       input_shape, expanded_gather_indices_shape, gather_dim_numbers));
   2576 
   2577   if (window_bounds.size() != input_shape.dimensions_size()) {
   2578     return InvalidArgument(
   2579         "Gather op must have one window bound for every input dimension; got: "
   2580         "len(window_bounds)=%lu, input_shape.rank=%d",
   2581         window_bounds.size(), input_shape.dimensions_size());
   2582   }
   2583 
   2584   if (window_bounds.size() !=
   2585       gather_dim_numbers.output_window_dims_size() +
   2586           gather_dim_numbers.elided_window_dims_size()) {
   2587     return InvalidArgument(
   2588         "All components of the window index in a gather op must either be a "
   2589         "output window index or explicitly elided; got len(window_bounds)=%lu, "
   2590         "output_window_bounds=%s, elided_window_bounds=%s",
   2591         window_bounds.size(),
   2592         Join(gather_dim_numbers.output_window_dims(), ",").c_str(),
   2593         Join(gather_dim_numbers.elided_window_dims(), ",").c_str());
   2594   }
   2595 
   2596   for (int i = 0; i < window_bounds.size(); i++) {
   2597     int64 window_bound = window_bounds[i];
   2598     int64 corresponding_input_bound = input_shape.dimensions(i);
   2599     if (window_bound < 0 || window_bound > corresponding_input_bound) {
   2600       return InvalidArgument(
   2601           "Window bound at index %d in gather op is out of range, must be "
   2602           "within "
   2603           "[0, %lld), got %lld",
   2604           i, corresponding_input_bound + 1, window_bound);
   2605     }
   2606   }
   2607 
   2608   for (int i = 0; i < gather_dim_numbers.elided_window_dims_size(); i++) {
   2609     if (window_bounds[gather_dim_numbers.elided_window_dims(i)] != 1) {
   2610       return InvalidArgument(
   2611           "Gather op can only elide window indices with bound 1, but bound is "
   2612           "%lld for index %lld at position %d",
   2613           window_bounds[gather_dim_numbers.elided_window_dims(i)],
   2614           gather_dim_numbers.elided_window_dims(i), i);
   2615     }
   2616   }
   2617 
   2618   int64 result_rank = gather_dim_numbers.output_window_dims_size() +
   2619                       (expanded_gather_indices_shape.size() - 1);
   2620   int64 window_dims_seen = 0;
   2621   int64 gather_dims_seen = 0;
   2622   std::vector<int64> output_dim_bounds;
   2623   output_dim_bounds.reserve(result_rank);
   2624   for (int64 i = 0; i < result_rank; i++) {
   2625     int64 current_bound;
   2626     bool is_window_index =
   2627         c_binary_search(gather_dim_numbers.output_window_dims(), i);
   2628     if (is_window_index) {
   2629       while (c_binary_search(gather_dim_numbers.elided_window_dims(),
   2630                              window_dims_seen)) {
   2631         window_dims_seen++;
   2632       }
   2633       current_bound = window_bounds[window_dims_seen++];
   2634     } else {
   2635       current_bound = expanded_gather_indices_shape[gather_dims_seen++];
   2636     }
   2637 
   2638     output_dim_bounds.push_back(current_bound);
   2639   }
   2640 
   2641   return ShapeUtil::MakeShape(input_shape.element_type(), output_dim_bounds);
   2642 }
   2643 
   2644 }  // namespace xla
   2645