| 159 | } |
| 160 | |
| 161 | void Compute(OpKernelContext *ctx) override { |
| 162 | const Tensor *indices_t, *values_t, *shape_t, *reduction_axes_t; |
| 163 | OP_REQUIRES_OK(ctx, ctx->input("input_indices", &indices_t)); |
| 164 | OP_REQUIRES_OK(ctx, ctx->input("input_values", &values_t)); |
| 165 | OP_REQUIRES_OK(ctx, ctx->input("input_shape", &shape_t)); |
| 166 | OP_REQUIRES_OK(ctx, ctx->input("reduction_axes", &reduction_axes_t)); |
| 167 | |
| 168 | OP_REQUIRES_OK(ctx, ValidateInputs(shape_t, reduction_axes_t)); |
| 169 | |
| 170 | // TODO(zongheng): we will call Reorder() below, which will modify |
| 171 | // in-place the underlying indices and values buffers. To avoid |
| 172 | // surprises of this kernel being stateful, we work around the above by |
| 173 | // making deep copies here. Remove this if/when we change Reorder()'s |
| 174 | // semantics. |
| 175 | const auto shape_vec = shape_t->vec<int64>(); |
| 176 | TensorShape shape; |
| 177 | OP_REQUIRES_OK(ctx, TensorShape::BuildTensorShape(shape_vec, &shape)); |
| 178 | |
| 179 | SparseTensor sp; |
| 180 | OP_REQUIRES_OK(ctx, SparseTensor::Create( |
| 181 | tensor::DeepCopy(*indices_t), tensor::DeepCopy(*values_t), |
| 182 | shape, &sp)); |
| 183 | ReduceDetails reduction = SparseTensorReduceHelper( |
| 184 | sp, reduction_axes_t->flat<int32>(), keep_dims_); |
| 185 | |
| 186 | Tensor *out_values; |
| 187 | OP_REQUIRES_OK( |
| 188 | ctx, ctx->allocate_output(0, reduction.reduced_shape, &out_values)); |
| 189 | auto out_flat = out_values->flat<T>(); |
| 190 | out_flat.setZero(); |
| 191 | |
| 192 | Tensor tmp_reduced_val; |
| 193 | OP_REQUIRES_OK(ctx, ctx->allocate_temp(DataTypeToEnum<T>::value, |
| 194 | TensorShape({}), &tmp_reduced_val)); |
| 195 | auto reduced_val = tmp_reduced_val.scalar<T>(); |
| 196 | |
| 197 | // Compute strides, and use it to convert coords to flat index. The |
| 198 | // coordinates returned by .group() have the same ndims as group_by_dims. |
| 199 | gtl::InlinedVector<int64, 8> output_strides(reduction.group_by_dims.size()); |
| 200 | if (!output_strides.empty()) { // Do this iff we don't reduce all. |
| 201 | output_strides.back() = 1; |
| 202 | for (int d = output_strides.size() - 2; d >= 0; --d) { |
| 203 | output_strides[d] = |
| 204 | output_strides[d + 1] * shape_vec(reduction.group_by_dims[d + 1]); |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | auto CoordinatesToFlatIndex = [](ArraySlice<int64> coords, |
| 209 | ArraySlice<int64> strides) { |
| 210 | if (strides.empty()) { // Reduce all. |
| 211 | return 0LL; |
| 212 | } |
| 213 | CHECK_EQ(coords.size(), strides.size()); |
| 214 | int64 idx = 0; |
| 215 | for (int i = 0; i < coords.size(); ++i) { |
| 216 | idx += coords[i] * strides[i]; |
| 217 | } |
| 218 | return idx; |
nothing calls this directly
no test coverage detected