| 506 | |
| 507 | template <typename T> |
| 508 | SparseTensor SparseTensor::Concat( |
| 509 | const gtl::ArraySlice<SparseTensor>& tensors) { |
| 510 | DCHECK_GE(tensors.size(), size_t{1}) << "Cannot concat 0 SparseTensors"; |
| 511 | const int dims = tensors[0].dims_; |
| 512 | DCHECK_GE(dims, 1) << "Cannot concat 0-dimensional SparseTensors"; |
| 513 | auto order_0 = tensors[0].order(); |
| 514 | const int primary_dim = order_0[0]; |
| 515 | ShapeArray final_order(order_0.begin(), order_0.end()); |
| 516 | ShapeArray final_shape(tensors[0].shape().begin(), tensors[0].shape().end()); |
| 517 | final_shape[primary_dim] = 0; // We'll build this up as we go along. |
| 518 | int num_entries = 0; |
| 519 | |
| 520 | bool fully_ordered = true; |
| 521 | for (const SparseTensor& st : tensors) { |
| 522 | DCHECK_EQ(st.dims_, dims) << "All SparseTensors must have the same rank."; |
| 523 | DCHECK_EQ(DataTypeToEnum<T>::v(), st.dtype()) |
| 524 | << "Concat requested with the wrong data type"; |
| 525 | DCHECK_GE(st.order()[0], 0) << "SparseTensor must be ordered"; |
| 526 | DCHECK_EQ(st.order()[0], primary_dim) |
| 527 | << "All SparseTensors' order[0] must match. This is the concat dim."; |
| 528 | if (st.order() != final_order) fully_ordered = false; |
| 529 | const VarDimArray& st_shape = st.shape(); |
| 530 | for (int d = 0; d < dims - 1; ++d) { |
| 531 | const int cdim = (d < primary_dim) ? d : d + 1; |
| 532 | DCHECK_EQ(final_shape[cdim], st_shape[cdim]) |
| 533 | << "All SparseTensors' shapes must match except on the concat dim. " |
| 534 | << "Concat dim: " << primary_dim |
| 535 | << ", mismatched shape at dim: " << cdim |
| 536 | << ". Expecting shape like: [" << str_util::Join(final_shape, ",") |
| 537 | << "] but saw shape: [" << str_util::Join(st_shape, ",") << "]"; |
| 538 | } |
| 539 | |
| 540 | // Update dimension of final shape |
| 541 | final_shape[primary_dim] = |
| 542 | (final_shape[primary_dim] + st_shape[primary_dim]); |
| 543 | |
| 544 | num_entries += st.num_entries(); // Update number of entries |
| 545 | } |
| 546 | |
| 547 | // If nonconsistent ordering among inputs, set final order to -1s. |
| 548 | if (!fully_ordered) { |
| 549 | final_order = UndefinedOrder(final_shape); |
| 550 | } |
| 551 | |
| 552 | Tensor output_ix(DT_INT64, TensorShape({num_entries, dims})); |
| 553 | Tensor output_vals(DataTypeToEnum<T>::v(), TensorShape({num_entries})); |
| 554 | |
| 555 | TTypes<int64>::Matrix ix_t = output_ix.matrix<int64>(); |
| 556 | typename TTypes<T>::Vec vals_t = output_vals.vec<T>(); |
| 557 | |
| 558 | Eigen::DenseIndex offset = 0; |
| 559 | int64 shape_offset = 0; |
| 560 | for (const SparseTensor& st : tensors) { |
| 561 | const int st_num_entries = st.num_entries(); |
| 562 | |
| 563 | // Fill in indices & values. |
| 564 | std::copy_n(&st.vals_.vec<T>()(0), st_num_entries, &vals_t(offset)); |
| 565 |
nothing calls this directly
no test coverage detected