code: def booster_fit(
      self,
      ctx: Context,
      bin_train_data: DataFrame,
      grad_and_hess: DataFrame,
      binning_dict: dict,
  ):
      # Initialization
      train_df = bin_train_data
      sample_pos = self._init_sample_pos(train_df)
      self._sample_on_leaves = sample_pos.empty_frame()
      root_node = self._initialize_root_node(
          ctx, train_df, grad_and_hess
      )
  
      # federated tree
      if not self._is_local_tree:
          # initialize homographic encryption
          if self._encrypt_kit is None:
              self._init_encrypt_kit(ctx)
          # Send Encrypted Grad and Hess
          self._send_gh(ctx, grad_and_hess)
  
          # send pack info
          send_pack_info = (
              {
                  "total_pack_num": self._pack_info["total_pack_num"],
                  "split_point_shift_bit": self._pack_info[
                      "split_point_shift_bit"
                  ],
                  "split_info_pack": self._split_info_pack,
              }
              if self._gh_pack
              else {}
          )
          ctx.hosts.put("pack_info", send_pack_info)
  
      # init histogram builder
      self.hist_builder = SBTHistogramBuilder(
          bin_train_data,
          binning_dict,
          None,
          None,
          hist_sub=self._hist_sub,
      )
  
      # init splitter
      self.splitter = SBTSplitter(
          bin_train_data,
          binning_dict,
          l2=self.l2,
          l1=self.l1,
          min_sample_split=self.min_sample_split,
          min_impurity_split=self.min_impurity_split,
          min_child_weight=self.min_child_weight,
          min_leaf_node=self.min_leaf_node,
      )
  
      # Prepare for training
      cur_layer_node = [root_node]
      grad_and_hess["cnt"] = 1
  
      for cur_depth, sub_ctx in ctx.on_iterations.ctxs_range(
          self.max_depth
      ):
          if len(cur_layer_node) == 0:
              logger.info("no nodes to split, stop training")
              break
  
          assert len(sample_pos) == len(
              train_df
          ), "sample pos len not match train data len, {} vs {}".format(
              len(sample_pos), len(train_df)
          )
          # debug checking code
          # self._check_assign_result(sample_pos, cur_layer_node)
          # initialize node map
          node_map = {
              n.nid: idx for idx, n in enumerate(cur_layer_node)
          }
          # compute histogram
          hist_inst, statistic_result = self.hist_builder.compute_hist(
              sub_ctx,
              cur_layer_node,
              train_df,
              grad_and_hess,
              sample_pos,
              node_map,
          )
          # compute best splits
          use_local_feat_only = (
              True if self._tree_mode == GUEST_FEAT_ONLY else False
          )
          split_info = self.splitter.split(
              sub_ctx,
              statistic_result,
              cur_layer_node,
              node_map,
              local_split=use_local_feat_only,
              sk=self._sk,
              coder=self._coder,
              gh_pack=self._gh_pack,
              pack_info=self._pack_info,
          )
          # update tree with best splits
          next_layer_nodes = self._update_tree(
              sub_ctx, cur_layer_node, split_info, train_df
          )
          # update feature importance
          self._update_feature_importance(sub_ctx, split_info, train_df)
          # sync nodes
          if not self._is_local_tree:
              self._sync_nodes(
                  sub_ctx, cur_layer_node, next_layer_nodes
              )
          # update sample positions
          sample_pos = self._update_sample_pos(
              sub_ctx,
              cur_layer_node,
              sample_pos,
              train_df,
              node_map,
              local_update=self._is_local_tree,
          )
          # if sample reaches leaf nodes, drop them
          sample_on_leaves = self._get_samples_on_leaves(sample_pos)
          (
              train_df,
              sample_pos,
              grad_and_hess,
          ) = self._drop_samples_on_leaves(
              sample_pos, train_df, grad_and_hess
          )
          self._sample_on_leaves = DataFrame.vstack(
              [self._sample_on_leaves, sample_on_leaves]
          )
          # next layer nodes
          cur_layer_node = next_layer_nodes
          logger.info(
              "layer {} done: next layer will split {} nodes, active samples num {}".format(
                  cur_depth, len(cur_layer_node), len(sample_pos)
              )
          )
          self.next_layer_node = next_layer_nodes
  
      # handle final leaves
      if len(cur_layer_node) != 0:
          for node in cur_layer_node:
              node.is_leaf = True
              node.sitename = ctx.local.name  # leaf always on guest
              self._nodes.append(node)
          self._sample_on_leaves = DataFrame.vstack(
              [self._sample_on_leaves, sample_pos]
          )
  
      # when training is done, all samples must be on leaves
      assert len(self._sample_on_leaves) == len(
          bin_train_data
      ), "sample on leaves num not match, {} vs {}".format(
          len(self._sample_on_leaves), len(bin_train_data)
      )
      # convert sample pos to weights
      self._sample_weights = self._convert_sample_pos_to_weight(
          self._sample_on_leaves, self._nodes
      )
      # convert bid to split value
      self._nodes = self._convert_bin_idx_to_split_val(
          ctx, self._nodes, binning_dict, bin_train_data.schema
      )
  
      # if is local tree, send mask nodes to host
      if self._is_local_tree:
          self._sync_nodes(ctx, self._nodes, [])
  
imports: from fate.ml.ensemble.learner.decision_tree.tree_core.decision_tree import DecisionTree, Node, _update_sample_pos_on_local_nodes, _merge_sample_pos
  from fate.ml.ensemble.learner.decision_tree.tree_core.hist import SBTHistogramBuilder
  from fate.ml.ensemble.learner.decision_tree.tree_core.splitter import SBTSplitter
  from fate.ml.ensemble.learner.decision_tree.tree_core.loss import get_task_info
  from fate.ml.ensemble.learner.decision_tree.tree_core.decision_tree import ALL_FEAT, GUEST_FEAT_ONLY
  from fate.ml.utils.predict_tools import BINARY, MULTI, REGRESSION
  from fate.arch import Context
  from fate.arch.dataframe import DataFrame
  from typing import List
  import functools
  import logging
  import pandas
  import torch
  import math
label_desc: Use image node_id 11 for calling the HeteroDecisionTreeGuest obj's underlying member method code with example usage: obj.booster_fit(ctx, bin_train_data, grad_and_hess, binning_dict) without return types