| 124 | bool IsReal() const override { return _Real; } |
| 125 | |
| 126 | void DoFFT(OpKernelContext* ctx, const Tensor& in, uint64* fft_shape, |
| 127 | Tensor* out) override { |
| 128 | // Create the axes (which are always trailing). |
| 129 | const auto axes = Eigen::ArrayXi::LinSpaced(FFTRank, 1, FFTRank); |
| 130 | auto device = ctx->eigen_device<CPUDevice>(); |
| 131 | |
| 132 | if (!IsReal()) { |
| 133 | // Compute the FFT using Eigen. |
| 134 | constexpr auto direction = |
| 135 | Forward ? Eigen::FFT_FORWARD : Eigen::FFT_REVERSE; |
| 136 | if (in.dtype() == DT_COMPLEX64) { |
| 137 | DCHECK_EQ(out->dtype(), DT_COMPLEX64); |
| 138 | auto input = Tensor(in).flat_inner_dims<complex64, FFTRank + 1>(); |
| 139 | auto output = out->flat_inner_dims<complex64, FFTRank + 1>(); |
| 140 | output.device(device) = |
| 141 | input.template fft<Eigen::BothParts, direction>(axes); |
| 142 | } else { |
| 143 | DCHECK_EQ(DT_COMPLEX128, in.dtype()); |
| 144 | DCHECK_EQ(DT_COMPLEX128, out->dtype()); |
| 145 | auto input = Tensor(in).flat_inner_dims<complex128, FFTRank + 1>(); |
| 146 | auto output = out->flat_inner_dims<complex128, FFTRank + 1>(); |
| 147 | output.device(device) = |
| 148 | input.template fft<Eigen::BothParts, direction>(axes); |
| 149 | } |
| 150 | } else { |
| 151 | if (IsForward()) { |
| 152 | auto input = Tensor(in).flat_inner_dims<float, FFTRank + 1>(); |
| 153 | const auto input_dims = input.dimensions(); |
| 154 | |
| 155 | // Slice input to fft_shape on its inner-most dimensions. |
| 156 | Eigen::DSizes<Eigen::DenseIndex, FFTRank + 1> input_slice_sizes; |
| 157 | input_slice_sizes[0] = input_dims[0]; |
| 158 | TensorShape temp_shape{input_dims[0]}; |
| 159 | for (int i = 1; i <= FFTRank; ++i) { |
| 160 | input_slice_sizes[i] = fft_shape[i - 1]; |
| 161 | temp_shape.AddDim(fft_shape[i - 1]); |
| 162 | } |
| 163 | |
| 164 | auto output = out->flat_inner_dims<complex64, FFTRank + 1>(); |
| 165 | const Eigen::DSizes<Eigen::DenseIndex, FFTRank + 1> zero_start_indices; |
| 166 | |
| 167 | // Compute the full FFT using a temporary tensor. |
| 168 | Tensor temp; |
| 169 | OP_REQUIRES_OK(ctx, ctx->allocate_temp(DataTypeToEnum<complex64>::v(), |
| 170 | temp_shape, &temp)); |
| 171 | auto full_fft = temp.flat_inner_dims<complex64, FFTRank + 1>(); |
| 172 | full_fft.device(device) = |
| 173 | input.slice(zero_start_indices, input_slice_sizes) |
| 174 | .template fft<Eigen::BothParts, Eigen::FFT_FORWARD>(axes); |
| 175 | |
| 176 | // Slice away the negative frequency components. |
| 177 | output.device(device) = |
| 178 | full_fft.slice(zero_start_indices, output.dimensions()); |
| 179 | } else { |
| 180 | // Reconstruct the full FFT and take the inverse. |
| 181 | auto input = Tensor(in).flat_inner_dims<complex64, FFTRank + 1>(); |
| 182 | auto output = out->flat_inner_dims<float, FFTRank + 1>(); |
| 183 | const auto input_dims = input.dimensions(); |
nothing calls this directly
no test coverage detected