| 31 | namespace tensorflow { |
| 32 | |
| 33 | void XlaBinaryOp::Compile(XlaOpKernelContext* ctx) { |
| 34 | const TensorShape lhs_shape = ctx->InputShape(0); |
| 35 | const TensorShape rhs_shape = ctx->InputShape(1); |
| 36 | |
| 37 | // By TensorFlow conventions the inputs may not have the same |
| 38 | // shapes, in which case they will be automatically broadcast if |
| 39 | // possible before mapping. Use the standard TensorFlow helper to |
| 40 | // compute valid broadcast shapes, but rely below on XLA to |
| 41 | // automatically perform the broadcast assuming its valid shapes are |
| 42 | // a superset of TensorFlow's valid shapes. |
| 43 | BCast bcast(BCast::FromShape(lhs_shape), BCast::FromShape(rhs_shape), |
| 44 | /*fewer_dims_optimization=*/false); |
| 45 | if (!bcast.IsValid()) { |
| 46 | ctx->SetStatus(errors::InvalidArgument("Incompatible shapes: ", |
| 47 | lhs_shape.DebugString(), " vs. ", |
| 48 | rhs_shape.DebugString())); |
| 49 | return; |
| 50 | } |
| 51 | |
| 52 | // Fetch the expressions containing the input tensors. |
| 53 | auto lhs_handle = ctx->Input(0); |
| 54 | auto rhs_handle = ctx->Input(1); |
| 55 | |
| 56 | // If the ranks of the inputs don't match, TensorFlow automatically |
| 57 | // reshapes the smaller by padding with dimensions of size 1 as a |
| 58 | // prefix. In other words to pad a 5-vector to a 3-dimensional |
| 59 | // tensor it is reshaped to have shape [1,1,5]. XLA's automatic |
| 60 | // broadcast code is able to broadcast from lower to higher rank, |
| 61 | // but doesn't assume you want to pad as a prefix of the dimensions, |
| 62 | // and instead needs to be told which dimensions of the higher rank |
| 63 | // tensor to match to the lower rank tensor. In this example it |
| 64 | // would be dimensions [2]. If we were matching a matrix against a |
| 65 | // 4-D tensor the dimensions to match would be [2,3], |
| 66 | // etc. extend_dimension encodes the general case. |
| 67 | std::vector<int64> extend_dimension; |
| 68 | int max_rank = std::max(lhs_shape.dims(), rhs_shape.dims()); |
| 69 | int min_rank = std::min(lhs_shape.dims(), rhs_shape.dims()); |
| 70 | if (min_rank != max_rank) { |
| 71 | for (int i = 0; i < min_rank; ++i) { |
| 72 | // Match the lower rank tensor along the larger-numbered |
| 73 | // dimensions of the higher rank tensor. |
| 74 | extend_dimension.push_back(max_rank - min_rank + i); |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | // Call virtual method to emit the computation. |
| 79 | xla::XlaOp output = |
| 80 | Computation(ctx, lhs_handle, lhs_shape.dim_sizes(), rhs_handle, |
| 81 | rhs_shape.dim_sizes(), bcast, extend_dimension); |
| 82 | |
| 83 | // The TensorFlow helper computed the post-broadcast shape in |
| 84 | // output_shape: we rely on subclassed Computations to implement the |
| 85 | // same broadcast semantics. |
| 86 | ctx->SetOutput(0, output); |
| 87 | } |
| 88 | |
| 89 | /* static */ std::pair<xla::XlaOp, xla::XlaOp> XlaBinaryOp::Broadcast( |
| 90 | xla::XlaOp lhs, xla::XlaOp rhs, const BCast& broadcast_helper) { |
nothing calls this directly
no test coverage detected