  void Compute(OpKernelContext* const context) override {
    // node_ids.
    const Tensor* node_ids_t;
    OP_REQUIRES_OK(context, context->input("node_ids", &node_ids_t));
    const auto node_ids = node_ids_t->vec<int32>();

    // gradients.
    const Tensor* gradients_t;
    OP_REQUIRES_OK(context, context->input("gradients", &gradients_t));
    const auto gradients = gradients_t->matrix<float>();

    // hessians.
    const Tensor* hessians_t;
    OP_REQUIRES_OK(context, context->input("hessians", &hessians_t));
    const auto hessians = hessians_t->matrix<float>();

    // feature indices.
    const Tensor* feature_indices_t;
    OP_REQUIRES_OK(context,
                   context->input("feature_indices", &feature_indices_t));
    const auto feature_indices = feature_indices_t->matrix<int32>();

    // feature values.
    const Tensor* feature_values_t;
    OP_REQUIRES_OK(context,
                   context->input("feature_values", &feature_values_t));
    const auto feature_values = feature_values_t->vec<int32>();

    // feature shape.
    const Tensor* feature_shape_t;
    OP_REQUIRES_OK(context, context->input("feature_shape", &feature_shape_t));
    OP_REQUIRES(context, TensorShapeUtils::IsVector(feature_shape_t->shape()),
                errors::InvalidArgument(
                    "Input shapes should be a vector but received shapes ",
                    feature_shape_t->shape().DebugString()));
    const auto feature_shape = feature_shape_t->vec<int32>();

    const int64_t batch_size = gradients_t->dim_size(0);
    const int64_t logits_dims = gradients_t->dim_size(1);
    const int64_t hessians_dims = hessians_t->dim_size(1);
    const int64_t stats_dims = logits_dims + hessians_dims;
    const int64_t num_sparse_entries = feature_indices_t->dim_size(0);
    const int32_t feature_dims = feature_shape(1);
    OP_REQUIRES(context, num_sparse_entries <= batch_size * feature_dims,
                errors::InvalidArgument(
                    "feature_indices dim0 should be <= gradients dim0 * "
                    "feature_shape[1]. features_indices dim0: ",
                    num_sparse_entries, " gradients dim0: ", batch_size,
                    ", feature_shape[1]: ", feature_dims));

    // Aggregate statistics info to map.
    StatsPartitionMap stats_map;

    int prev_instance = 0;
    int prev_f_dim = -1;

    for (int i = 0; i < num_sparse_entries; ++i) {
      // the instance number within a batch
      const int32_t instance = feature_indices(i, 0);
      DCHECK_LE(instance, batch_size);
      DCHECK_GE(instance, prev_instance);
      // the node id within a tree.
      const int32_t node_id = node_ids(instance);
      DCHECK_LE(node_id, max_splits_);
      // the feature dimension.
      const int32_t f_dim = feature_indices(i, 1);
      DCHECK_LE(f_dim, feature_dims);
      // the bucket id of the value.
      const int32_t bucket_id = feature_values(i);
      DCHECK_LE(bucket_id, num_buckets_);

      // Add statistics for the missing entries into default bucket.
      // The last bucket is default bucket.
      const int missing_entry_bucket = num_buckets_;
      AddRangeStats(prev_instance, instance, prev_f_dim, f_dim, &stats_map,
                    gradients, hessians, node_ids, feature_dims,
                    missing_entry_bucket, logits_dims, stats_dims);
      prev_instance = instance;
      prev_f_dim = f_dim;
      // Add statistics for the non-missing entry into
      // (cur_instance, cur_f_dim, bucket_id).
      AddInstanceStatsToMap(instance, f_dim, bucket_id, logits_dims, stats_dims,
                            &stats_map, gradients, hessians, node_ids);
    }
    AddRangeStats(prev_instance, batch_size - 1, prev_f_dim, feature_dims,
                  &stats_map, gradients, hessians, node_ids, feature_dims,
                  num_buckets_, logits_dims, stats_dims);

    // Serialize statistics info map to tensor output.
    const int64_t num_slots = stats_map.size() * stats_dims;
    Tensor* summary_indices_t = nullptr;
    OP_REQUIRES_OK(context,
                   context->allocate_output("stats_summary_indices",
                                            TensorShape({num_slots, 4}),
                                            &summary_indices_t));
    auto summary_indices = summary_indices_t->matrix<int32>();
    Tensor* summary_values_t = nullptr;
    OP_REQUIRES_OK(context, context->allocate_output("stats_summary_values",
                                                     TensorShape({num_slots}),
                                                     &summary_values_t));
    auto summary_values = summary_values_t->vec<float>();
    int entry_index = 0;
    for (auto& iter : stats_map) {
      for (int stat_dim = 0; stat_dim < stats_dims; ++stat_dim) {
        summary_indices(entry_index, 0) = iter.first.node_id;
        summary_indices(entry_index, 1) = iter.first.feature_dim;
        summary_indices(entry_index, 2) = iter.first.bucket_id;
        summary_indices(entry_index, 3) = stat_dim;
        summary_values(entry_index) = iter.second[stat_dim];
        ++entry_index;
      }
    }

    Tensor* summary_shape_t = nullptr;
    OP_REQUIRES_OK(
        context, context->allocate_output("stats_summary_shape",
                                          TensorShape({4}), &summary_shape_t));
    auto summary_shape = summary_shape_t->vec<int32>();
    summary_shape(0) = max_splits_;
    summary_shape(1) = feature_dims;
    summary_shape(2) = num_buckets_ + 1;
    summary_shape(3) = stats_dims;
  }
  void Compute(OpKernelContext* const context) override {
     
    const Tensor* node_ids_t;
    OP_REQUIRES_OK(context, context->input("node_ids", &node_ids_t));
    const auto node_ids = node_ids_t->vec<int32>();

     
    const Tensor* gradients_t;
    OP_REQUIRES_OK(context, context->input("gradients", &gradients_t));
    const auto gradients = gradients_t->matrix<float>();

     
    const Tensor* hessians_t;
    OP_REQUIRES_OK(context, context->input("hessians", &hessians_t));
    const auto hessians = hessians_t->matrix<float>();

     
    const Tensor* feature_indices_t;
    OP_REQUIRES_OK(context,
                   context->input("feature_indices", &feature_indices_t));
    const auto feature_indices = feature_indices_t->matrix<int32>();

     
    const Tensor* feature_values_t;
    OP_REQUIRES_OK(context,
                   context->input("feature_values", &feature_values_t));
    const auto feature_values = feature_values_t->vec<int32>();

     
    const Tensor* feature_shape_t;
    OP_REQUIRES_OK(context, context->input("feature_shape", &feature_shape_t));
    OP_REQUIRES(context, TensorShapeUtils::IsVector(feature_shape_t->shape()),
                errors::InvalidArgument(
                    "Input shapes should be a vector but received shapes ",
                    feature_shape_t->shape().DebugString()));
    const auto feature_shape = feature_shape_t->vec<int32>();

    const int64_t batch_size = gradients_t->dim_size(0);
    const int64_t logits_dims = gradients_t->dim_size(1);
    const int64_t hessians_dims = hessians_t->dim_size(1);
    const int64_t stats_dims = logits_dims + hessians_dims;
    const int64_t num_sparse_entries = feature_indices_t->dim_size(0);
    const int32_t feature_dims = feature_shape(1);
    OP_REQUIRES(context, num_sparse_entries <= batch_size * feature_dims,
                errors::InvalidArgument(
                    "feature_indices dim0 should be <= gradients dim0 * "
                    "feature_shape[1]. features_indices dim0: ",
                    num_sparse_entries, " gradients dim0: ", batch_size,
                    ", feature_shape[1]: ", feature_dims));

     
    StatsPartitionMap stats_map;

    int prev_instance = 0;
    int prev_f_dim = -1;

    for (int i = 0; i < num_sparse_entries; ++i) {
       
      const int32_t instance = feature_indices(i, 0);
      DCHECK_LE(instance, batch_size);
      DCHECK_GE(instance, prev_instance);
       
      const int32_t node_id = node_ids(instance);
      DCHECK_LE(node_id, max_splits_);
       
      const int32_t f_dim = feature_indices(i, 1);
      DCHECK_LE(f_dim, feature_dims);
       
      const int32_t bucket_id = feature_values(i);
      DCHECK_LE(bucket_id, num_buckets_);

       
       
      const int missing_entry_bucket = num_buckets_;
      AddRangeStats(prev_instance, instance, prev_f_dim, f_dim, &stats_map,
                    gradients, hessians, node_ids, feature_dims,
                    missing_entry_bucket, logits_dims, stats_dims);
      prev_instance = instance;
      prev_f_dim = f_dim;
       
       
      AddInstanceStatsToMap(instance, f_dim, bucket_id, logits_dims, stats_dims,
                            &stats_map, gradients, hessians, node_ids);
    }
    AddRangeStats(prev_instance, batch_size - 1, prev_f_dim, feature_dims,
                  &stats_map, gradients, hessians, node_ids, feature_dims,
                  num_buckets_, logits_dims, stats_dims);

     
    const int64_t num_slots = stats_map.size() * stats_dims;
    Tensor* summary_indices_t = nullptr;
    OP_REQUIRES_OK(context,
                   context->allocate_output("stats_summary_indices",
                                            TensorShape({num_slots, 4}),
                                            &summary_indices_t));
    auto summary_indices = summary_indices_t->matrix<int32>();
    Tensor* summary_values_t = nullptr;
    OP_REQUIRES_OK(context, context->allocate_output("stats_summary_values",
                                                     TensorShape({num_slots}),
                                                     &summary_values_t));
    auto summary_values = summary_values_t->vec<float>();
    int entry_index = 0;
    for (auto& iter : stats_map) {
      for (int stat_dim = 0; stat_dim < stats_dims; ++stat_dim) {
        summary_indices(entry_index, 0) = iter.first.node_id;
        summary_indices(entry_index, 1) = iter.first.feature_dim;
        summary_indices(entry_index, 2) = iter.first.bucket_id;
        summary_indices(entry_index, 3) = stat_dim;
        summary_values(entry_index) = iter.second[stat_dim];
        ++entry_index;
      }
    }

    Tensor* summary_shape_t = nullptr;
    OP_REQUIRES_OK(
        context, context->allocate_output("stats_summary_shape",
                                          TensorShape({4}), &summary_shape_t));
    auto summary_shape = summary_shape_t->vec<int32>();
    summary_shape(0) = max_splits_;
    summary_shape(1) = feature_dims;
    summary_shape(2) = num_buckets_ + 1;
    summary_shape(3) = stats_dims;
  }