| 79 | } |
| 80 | |
| 81 | static void Run(const OpKernelContext* context, const Tensor& in_x, |
| 82 | const Tensor in_y, bool adj_x, bool adj_y, bool trans_x, |
| 83 | bool trans_y, const MatMulBCast& bcast, Tensor* out, |
| 84 | int start, int limit) { |
| 85 | static_assert(IsComplex, "Complex type expected."); |
| 86 | auto Tx = in_x.tensor<Scalar, 3>(); |
| 87 | auto Ty = in_y.tensor<Scalar, 3>(); |
| 88 | auto Tz = out->tensor<Scalar, 3>(); |
| 89 | // We use the identities |
| 90 | // conj(a) * conj(b) = conj(a * b) |
| 91 | // conj(a) * b = conj(a * conj(b)) |
| 92 | // to halve the number of cases. The final conjugation of the result is |
| 93 | // done at the end of LaunchBatchMatMul<CPUDevice, Scalar>::Launch(). |
| 94 | Eigen::array<Eigen::IndexPair<Eigen::DenseIndex>, 1> contract_pairs; |
| 95 | contract_pairs[0] = ContractionDims(adj_x || trans_x, adj_y || trans_y); |
| 96 | const Eigen::ThreadPoolDevice d = context->eigen_cpu_device(); |
| 97 | |
| 98 | const bool should_bcast = bcast.IsBroadcastingRequired(); |
| 99 | const auto& x_batch_indices = bcast.x_batch_indices(); |
| 100 | const auto& y_batch_indices = bcast.y_batch_indices(); |
| 101 | for (int64 i = start; i < limit; ++i) { |
| 102 | const int64 x_batch_index = should_bcast ? x_batch_indices[i] : i; |
| 103 | const int64 y_batch_index = should_bcast ? y_batch_indices[i] : i; |
| 104 | |
| 105 | auto x = Tx.template chip<0>(x_batch_index); |
| 106 | auto z = Tz.template chip<0>(i); |
| 107 | if (adj_x != adj_y) { |
| 108 | auto y = Ty.template chip<0>(y_batch_index).conjugate(); |
| 109 | z.device(d) = x.contract(y, contract_pairs); |
| 110 | } else { |
| 111 | auto y = Ty.template chip<0>(y_batch_index); |
| 112 | z.device(d) = x.contract(y, contract_pairs); |
| 113 | } |
| 114 | } |
| 115 | } |
| 116 | }; |
| 117 | |
| 118 | // The Eigen contraction kernel used here is very large and slow to compile, |
nothing calls this directly
no test coverage detected