| 600 | } |
| 601 | |
| 602 | StatusOr<Literal> LiteralBase::Broadcast( |
| 603 | const Shape& result_shape, absl::Span<const int64> dimensions) const { |
| 604 | if (!shape().IsArray()) { |
| 605 | return InvalidArgument("Broadcast only supports arrays."); |
| 606 | } |
| 607 | |
| 608 | for (int64 i = 0; i < dimensions.size(); i++) { |
| 609 | TF_RET_CHECK(shape().dimensions(i) == |
| 610 | result_shape.dimensions(dimensions[i])); |
| 611 | } |
| 612 | |
| 613 | Literal result(result_shape); |
| 614 | |
| 615 | // scratch_source_index is temporary storage space for the computed index into |
| 616 | // the input literal. We put it here to avoid allocating an std::vector in |
| 617 | // every iteration of ShapeUtil::ForEachIndex. |
| 618 | std::vector<int64> scratch_source_index(shape().dimensions_size()); |
| 619 | |
| 620 | char* dest_data = static_cast<char*>(result.untyped_data()); |
| 621 | const char* source_data = static_cast<const char*>(untyped_data()); |
| 622 | const int64 primitive_size = |
| 623 | ShapeUtil::ByteSizeOfPrimitiveType(shape().element_type()); |
| 624 | |
| 625 | ShapeUtil::ForEachIndex( |
| 626 | result_shape, [&](absl::Span<const int64> output_index) { |
| 627 | for (int64 i = 0; i < dimensions.size(); ++i) { |
| 628 | scratch_source_index[i] = output_index[dimensions[i]]; |
| 629 | } |
| 630 | int64 dest_index = IndexUtil::MultidimensionalIndexToLinearIndex( |
| 631 | result_shape, output_index); |
| 632 | int64 source_index = IndexUtil::MultidimensionalIndexToLinearIndex( |
| 633 | shape(), scratch_source_index); |
| 634 | memcpy(dest_data + primitive_size * dest_index, |
| 635 | source_data + primitive_size * source_index, primitive_size); |
| 636 | return true; |
| 637 | }); |
| 638 | |
| 639 | return std::move(result); |
| 640 | } |
| 641 | |
| 642 | StatusOr<Literal> LiteralBase::Reshape( |
| 643 | absl::Span<const int64> dimensions) const { |
nothing calls this directly
no test coverage detected