| 285 | explicit ConcatOffsetOp(OpKernelConstruction* ctx) : OpKernel(ctx) {} |
| 286 | |
| 287 | void Compute(OpKernelContext* ctx) override { |
| 288 | const Tensor& concat_dim = ctx->input(0); |
| 289 | OP_REQUIRES( |
| 290 | ctx, IsLegacyScalar(concat_dim.shape()), |
| 291 | errors::InvalidArgument( |
| 292 | "Concat dim tensor should be a scalar integer, but got shape ", |
| 293 | concat_dim.shape().DebugString())); |
| 294 | for (int i = 1; i < ctx->num_inputs(); ++i) { |
| 295 | const Tensor& inp = ctx->input(i); |
| 296 | OP_REQUIRES(ctx, TensorShapeUtils::IsVector(inp.shape()), |
| 297 | errors::InvalidArgument("input ", i, |
| 298 | " should be a vector, but got shape ", |
| 299 | inp.shape().DebugString())); |
| 300 | } |
| 301 | // Suppose a Concat() op needs to Concatenate N tensors, each of |
| 302 | // which has the same number of dimensions. Their shapes match |
| 303 | // except the concat dimension. |
| 304 | // |
| 305 | // E.g., say, we want to concatenate 3 tensors in the 2nd |
| 306 | // dimension, and their shapes are: |
| 307 | // |
| 308 | // [2, 2, 5, 7] |
| 309 | // [2, 3, 5, 7] |
| 310 | // [2, 4, 5, 7] |
| 311 | // |
| 312 | // Here, N=3, cdim=1, dims=4. The concatenated tensor has shape |
| 313 | // [2,9,5,7]. We will compute the cumulative sum along the 2nd |
| 314 | // dimension to figure out each input's offset in the concatenated |
| 315 | // output: |
| 316 | // [0, 0, 0, 0] |
| 317 | // [0, 2, 0, 0] |
| 318 | // [0, 5, 0, 0] |
| 319 | const int32 N = ctx->num_inputs() - 1; |
| 320 | const Tensor& inp0 = ctx->input(1); |
| 321 | auto inp0_vec = inp0.vec<int32>(); |
| 322 | const int64 cdim = internal::SubtleMustCopy(concat_dim.scalar<int32>()()); |
| 323 | const int64 dims = inp0.NumElements(); |
| 324 | int32 axis = cdim < 0 ? cdim + dims : cdim; |
| 325 | OP_REQUIRES(ctx, FastBoundsCheck(axis, dims), |
| 326 | errors::InvalidArgument("Concat dim is out of range: ", cdim, |
| 327 | " vs. ", dims)); |
| 328 | int32 offset = 0; |
| 329 | for (int i = 0; i < N; ++i) { |
| 330 | const Tensor& inp = ctx->input(1 + i); |
| 331 | OP_REQUIRES( |
| 332 | ctx, dims == inp.NumElements(), |
| 333 | errors::InvalidArgument("input ", i, " should contain ", dims, |
| 334 | " elements, but got ", inp.NumElements())); |
| 335 | auto inp_vec = inp.vec<int32>(); |
| 336 | Tensor* out = nullptr; |
| 337 | OP_REQUIRES_OK(ctx, ctx->allocate_output(i, {dims}, &out)); |
| 338 | auto out_vec = out->vec<int32>(); |
| 339 | for (int64 j = 0; j < dims; ++j) { |
| 340 | if (j == axis) { |
| 341 | out_vec(j) = offset; |
| 342 | offset += inp_vec(j); |
| 343 | } else { |
| 344 | OP_REQUIRES(ctx, (inp0_vec(j) == inp_vec(j)), |
nothing calls this directly
no test coverage detected