def training_graph(self, input_data, input_labels, random_seed, data_spec, epoch=None):
    'Constructs a TF graph for training a random tree.\n\n    Args:\n      input_data: A tensor or SparseTensor or placeholder for input data.\n      input_labels: A tensor or placeholder for labels associated with\n        input_data.\n      random_seed: The random number generator seed to use for this tree.  0\n        means use the current time as the seed.\n      data_spec: A list of tf.dtype values specifying the original types of\n        each column.\n      epoch: A tensor or placeholder for the epoch the training data comes from.\n\n    Returns:\n      The last op in the random tree training graph.\n    '
    epoch = ([0] if (epoch is None) else epoch)
    sparse_indices = []
    sparse_values = []
    sparse_shape = []
    if isinstance(input_data, ops.SparseTensor):
        sparse_indices = input_data.indices
        sparse_values = input_data.values
        sparse_shape = input_data.shape
        input_data = []
    (node_sums, node_squares, splits_indices, splits_sums, splits_squares, totals_indices, totals_sums, totals_squares, input_leaves) = self.training_ops.count_extremely_random_stats(input_data, sparse_indices, sparse_values, sparse_shape, data_spec, input_labels, self.variables.tree, self.variables.tree_thresholds, self.variables.node_to_accumulator_map, self.variables.candidate_split_features, self.variables.candidate_split_thresholds, self.variables.start_epoch, epoch, num_classes=self.params.num_output_columns, regression=self.params.regression)
    node_update_ops = []
    node_update_ops.append(state_ops.assign_add(self.variables.node_sums, node_sums))
    splits_update_ops = []
    splits_update_ops.append(self.training_ops.scatter_add_ndim(self.variables.candidate_split_sums, splits_indices, splits_sums))
    splits_update_ops.append(self.training_ops.scatter_add_ndim(self.variables.accumulator_sums, totals_indices, totals_sums))
    if self.params.regression:
        node_update_ops.append(state_ops.assign_add(self.variables.node_squares, node_squares))
        splits_update_ops.append(self.training_ops.scatter_add_ndim(self.variables.candidate_split_squares, splits_indices, splits_squares))
        splits_update_ops.append(self.training_ops.scatter_add_ndim(self.variables.accumulator_squares, totals_indices, totals_squares))
    (update_indices, feature_updates, threshold_updates) = self.training_ops.sample_inputs(input_data, sparse_indices, sparse_values, sparse_shape, self.variables.node_to_accumulator_map, input_leaves, self.variables.candidate_split_features, self.variables.candidate_split_thresholds, split_initializations_per_input=self.params.split_initializations_per_input, split_sampling_random_seed=random_seed)
    update_features_op = state_ops.scatter_update(self.variables.candidate_split_features, update_indices, feature_updates)
    update_thresholds_op = state_ops.scatter_update(self.variables.candidate_split_thresholds, update_indices, threshold_updates)
    with ops.control_dependencies(splits_update_ops):
        children = array_ops.squeeze(array_ops.slice(self.variables.tree, [0, 0], [(- 1), 1]), squeeze_dims=[1])
        is_leaf = math_ops.equal(constants.LEAF_NODE, children)
        leaves = math_ops.to_int32(array_ops.squeeze(array_ops.where(is_leaf), squeeze_dims=[1]))
        (finished, stale) = self.training_ops.finished_nodes(leaves, self.variables.node_to_accumulator_map, self.variables.candidate_split_sums, self.variables.candidate_split_squares, self.variables.accumulator_sums, self.variables.accumulator_squares, self.variables.start_epoch, epoch, num_split_after_samples=self.params.split_after_samples, min_split_samples=self.params.min_split_samples)
    non_fertile_leaves = array_ops.boolean_mask(leaves, math_ops.less(array_ops.gather(self.variables.node_to_accumulator_map, leaves), 0))
    with ops.control_dependencies(node_update_ops):
        sums = array_ops.gather(self.variables.node_sums, non_fertile_leaves)
        if self.params.regression:
            squares = array_ops.gather(self.variables.node_squares, non_fertile_leaves)
            non_fertile_leaf_scores = self._variance(sums, squares)
        else:
            non_fertile_leaf_scores = self._weighted_gini(sums)
    with ops.control_dependencies(splits_update_ops):
        split_indices = self.training_ops.best_splits(finished, self.variables.node_to_accumulator_map, self.variables.candidate_split_sums, self.variables.candidate_split_squares, self.variables.accumulator_sums, self.variables.accumulator_squares, regression=self.params.regression)
    with ops.control_dependencies([update_features_op, update_thresholds_op]):
        (tree_update_indices, tree_children_updates, tree_threshold_updates, tree_depth_updates, new_eot) = self.training_ops.grow_tree(self.variables.end_of_tree, self.variables.tree_depths, self.variables.node_to_accumulator_map, finished, split_indices, self.variables.candidate_split_features, self.variables.candidate_split_thresholds)
        tree_update_op = state_ops.scatter_update(self.variables.tree, tree_update_indices, tree_children_updates)
        thresholds_update_op = state_ops.scatter_update(self.variables.tree_thresholds, tree_update_indices, tree_threshold_updates)
        depth_update_op = state_ops.scatter_update(self.variables.tree_depths, tree_update_indices, tree_depth_updates)
        new_epoch_updates = (epoch * array_ops.ones_like(tree_depth_updates))
        epoch_update_op = state_ops.scatter_update(self.variables.start_epoch, tree_update_indices, new_epoch_updates)
    with ops.control_dependencies([depth_update_op]):
        (node_map_updates, accumulators_cleared, accumulators_allocated) = self.training_ops.update_fertile_slots(finished, non_fertile_leaves, non_fertile_leaf_scores, self.variables.end_of_tree, self.variables.tree_depths, self.variables.accumulator_sums, self.variables.node_to_accumulator_map, stale, max_depth=self.params.max_depth, regression=self.params.regression)
    (gated_new_eot,) = control_flow_ops.tuple([new_eot], control_inputs=[node_map_updates])
    eot_update_op = state_ops.assign(self.variables.end_of_tree, gated_new_eot)
    updates = []
    updates.append(eot_update_op)
    updates.append(tree_update_op)
    updates.append(thresholds_update_op)
    updates.append(epoch_update_op)
    updates.append(state_ops.scatter_update(self.variables.node_to_accumulator_map, array_ops.squeeze(array_ops.slice(node_map_updates, [0, 0], [1, (- 1)]), squeeze_dims=[0]), array_ops.squeeze(array_ops.slice(node_map_updates, [1, 0], [1, (- 1)]), squeeze_dims=[0])))
    cleared_and_allocated_accumulators = array_ops.concat(0, [accumulators_cleared, accumulators_allocated])
    split_values = array_ops.tile(array_ops.expand_dims(array_ops.expand_dims(array_ops.zeros_like(cleared_and_allocated_accumulators, dtype=dtypes.float32), 1), 2), [1, self.params.num_splits_to_consider, self.params.num_output_columns])
    updates.append(state_ops.scatter_update(self.variables.candidate_split_sums, cleared_and_allocated_accumulators, split_values))
    if self.params.regression:
        updates.append(state_ops.scatter_update(self.variables.candidate_split_squares, cleared_and_allocated_accumulators, split_values))
    total_cleared = array_ops.tile(array_ops.expand_dims(math_ops.neg(array_ops.ones_like(accumulators_cleared, dtype=dtypes.float32)), 1), [1, self.params.num_output_columns])
    total_reset = array_ops.tile(array_ops.expand_dims(array_ops.zeros_like(accumulators_allocated, dtype=dtypes.float32), 1), [1, self.params.num_output_columns])
    accumulator_updates = array_ops.concat(0, [total_cleared, total_reset])
    updates.append(state_ops.scatter_update(self.variables.accumulator_sums, cleared_and_allocated_accumulators, accumulator_updates))
    if self.params.regression:
        updates.append(state_ops.scatter_update(self.variables.accumulator_squares, cleared_and_allocated_accumulators, accumulator_updates))
    split_features_updates = array_ops.tile(array_ops.expand_dims(math_ops.neg(array_ops.ones_like(cleared_and_allocated_accumulators)), 1), [1, self.params.num_splits_to_consider])
    updates.append(state_ops.scatter_update(self.variables.candidate_split_features, cleared_and_allocated_accumulators, split_features_updates))
    updates += self.finish_iteration()
    return control_flow_ops.group(*updates)
Constructs a TF graph for training a random tree.

Args:
  input_data: A tensor or SparseTensor or placeholder for input data.
  input_labels: A tensor or placeholder for labels associated with
    input_data.
  random_seed: The random number generator seed to use for this tree.  0
    means use the current time as the seed.
  data_spec: A list of tf.dtype values specifying the original types of
    each column.
  epoch: A tensor or placeholder for the epoch the training data comes from.

Returns:
  The last op in the random tree training graph.
def training_graph(self, input_data, input_labels, random_seed, data_spec, epoch=None):
    'Constructs a TF graph for training a random tree.\n\n    Args:\n      input_data: A tensor or SparseTensor or placeholder for input data.\n      input_labels: A tensor or placeholder for labels associated with\n        input_data.\n      random_seed: The random number generator seed to use for this tree.  0\n        means use the current time as the seed.\n      data_spec: A list of tf.dtype values specifying the original types of\n        each column.\n      epoch: A tensor or placeholder for the epoch the training data comes from.\n\n    Returns:\n      The last op in the random tree training graph.\n    '
    epoch = ([0] if (epoch is None) else epoch)
    sparse_indices = []
    sparse_values = []
    sparse_shape = []
    if isinstance(input_data, ops.SparseTensor):
        sparse_indices = input_data.indices
        sparse_values = input_data.values
        sparse_shape = input_data.shape
        input_data = []
    (node_sums, node_squares, splits_indices, splits_sums, splits_squares, totals_indices, totals_sums, totals_squares, input_leaves) = self.training_ops.count_extremely_random_stats(input_data, sparse_indices, sparse_values, sparse_shape, data_spec, input_labels, self.variables.tree, self.variables.tree_thresholds, self.variables.node_to_accumulator_map, self.variables.candidate_split_features, self.variables.candidate_split_thresholds, self.variables.start_epoch, epoch, num_classes=self.params.num_output_columns, regression=self.params.regression)
    node_update_ops = []
    node_update_ops.append(state_ops.assign_add(self.variables.node_sums, node_sums))
    splits_update_ops = []
    splits_update_ops.append(self.training_ops.scatter_add_ndim(self.variables.candidate_split_sums, splits_indices, splits_sums))
    splits_update_ops.append(self.training_ops.scatter_add_ndim(self.variables.accumulator_sums, totals_indices, totals_sums))
    if self.params.regression:
        node_update_ops.append(state_ops.assign_add(self.variables.node_squares, node_squares))
        splits_update_ops.append(self.training_ops.scatter_add_ndim(self.variables.candidate_split_squares, splits_indices, splits_squares))
        splits_update_ops.append(self.training_ops.scatter_add_ndim(self.variables.accumulator_squares, totals_indices, totals_squares))
    (update_indices, feature_updates, threshold_updates) = self.training_ops.sample_inputs(input_data, sparse_indices, sparse_values, sparse_shape, self.variables.node_to_accumulator_map, input_leaves, self.variables.candidate_split_features, self.variables.candidate_split_thresholds, split_initializations_per_input=self.params.split_initializations_per_input, split_sampling_random_seed=random_seed)
    update_features_op = state_ops.scatter_update(self.variables.candidate_split_features, update_indices, feature_updates)
    update_thresholds_op = state_ops.scatter_update(self.variables.candidate_split_thresholds, update_indices, threshold_updates)
    with ops.control_dependencies(splits_update_ops):
        children = array_ops.squeeze(array_ops.slice(self.variables.tree, [0, 0], [(- 1), 1]), squeeze_dims=[1])
        is_leaf = math_ops.equal(constants.LEAF_NODE, children)
        leaves = math_ops.to_int32(array_ops.squeeze(array_ops.where(is_leaf), squeeze_dims=[1]))
        (finished, stale) = self.training_ops.finished_nodes(leaves, self.variables.node_to_accumulator_map, self.variables.candidate_split_sums, self.variables.candidate_split_squares, self.variables.accumulator_sums, self.variables.accumulator_squares, self.variables.start_epoch, epoch, num_split_after_samples=self.params.split_after_samples, min_split_samples=self.params.min_split_samples)
    non_fertile_leaves = array_ops.boolean_mask(leaves, math_ops.less(array_ops.gather(self.variables.node_to_accumulator_map, leaves), 0))
    with ops.control_dependencies(node_update_ops):
        sums = array_ops.gather(self.variables.node_sums, non_fertile_leaves)
        if self.params.regression:
            squares = array_ops.gather(self.variables.node_squares, non_fertile_leaves)
            non_fertile_leaf_scores = self._variance(sums, squares)
        else:
            non_fertile_leaf_scores = self._weighted_gini(sums)
    with ops.control_dependencies(splits_update_ops):
        split_indices = self.training_ops.best_splits(finished, self.variables.node_to_accumulator_map, self.variables.candidate_split_sums, self.variables.candidate_split_squares, self.variables.accumulator_sums, self.variables.accumulator_squares, regression=self.params.regression)
    with ops.control_dependencies([update_features_op, update_thresholds_op]):
        (tree_update_indices, tree_children_updates, tree_threshold_updates, tree_depth_updates, new_eot) = self.training_ops.grow_tree(self.variables.end_of_tree, self.variables.tree_depths, self.variables.node_to_accumulator_map, finished, split_indices, self.variables.candidate_split_features, self.variables.candidate_split_thresholds)
        tree_update_op = state_ops.scatter_update(self.variables.tree, tree_update_indices, tree_children_updates)
        thresholds_update_op = state_ops.scatter_update(self.variables.tree_thresholds, tree_update_indices, tree_threshold_updates)
        depth_update_op = state_ops.scatter_update(self.variables.tree_depths, tree_update_indices, tree_depth_updates)
        new_epoch_updates = (epoch * array_ops.ones_like(tree_depth_updates))
        epoch_update_op = state_ops.scatter_update(self.variables.start_epoch, tree_update_indices, new_epoch_updates)
    with ops.control_dependencies([depth_update_op]):
        (node_map_updates, accumulators_cleared, accumulators_allocated) = self.training_ops.update_fertile_slots(finished, non_fertile_leaves, non_fertile_leaf_scores, self.variables.end_of_tree, self.variables.tree_depths, self.variables.accumulator_sums, self.variables.node_to_accumulator_map, stale, max_depth=self.params.max_depth, regression=self.params.regression)
    (gated_new_eot,) = control_flow_ops.tuple([new_eot], control_inputs=[node_map_updates])
    eot_update_op = state_ops.assign(self.variables.end_of_tree, gated_new_eot)
    updates = []
    updates.append(eot_update_op)
    updates.append(tree_update_op)
    updates.append(thresholds_update_op)
    updates.append(epoch_update_op)
    updates.append(state_ops.scatter_update(self.variables.node_to_accumulator_map, array_ops.squeeze(array_ops.slice(node_map_updates, [0, 0], [1, (- 1)]), squeeze_dims=[0]), array_ops.squeeze(array_ops.slice(node_map_updates, [1, 0], [1, (- 1)]), squeeze_dims=[0])))
    cleared_and_allocated_accumulators = array_ops.concat(0, [accumulators_cleared, accumulators_allocated])
    split_values = array_ops.tile(array_ops.expand_dims(array_ops.expand_dims(array_ops.zeros_like(cleared_and_allocated_accumulators, dtype=dtypes.float32), 1), 2), [1, self.params.num_splits_to_consider, self.params.num_output_columns])
    updates.append(state_ops.scatter_update(self.variables.candidate_split_sums, cleared_and_allocated_accumulators, split_values))
    if self.params.regression:
        updates.append(state_ops.scatter_update(self.variables.candidate_split_squares, cleared_and_allocated_accumulators, split_values))
    total_cleared = array_ops.tile(array_ops.expand_dims(math_ops.neg(array_ops.ones_like(accumulators_cleared, dtype=dtypes.float32)), 1), [1, self.params.num_output_columns])
    total_reset = array_ops.tile(array_ops.expand_dims(array_ops.zeros_like(accumulators_allocated, dtype=dtypes.float32), 1), [1, self.params.num_output_columns])
    accumulator_updates = array_ops.concat(0, [total_cleared, total_reset])
    updates.append(state_ops.scatter_update(self.variables.accumulator_sums, cleared_and_allocated_accumulators, accumulator_updates))
    if self.params.regression:
        updates.append(state_ops.scatter_update(self.variables.accumulator_squares, cleared_and_allocated_accumulators, accumulator_updates))
    split_features_updates = array_ops.tile(array_ops.expand_dims(math_ops.neg(array_ops.ones_like(cleared_and_allocated_accumulators)), 1), [1, self.params.num_splits_to_consider])
    updates.append(state_ops.scatter_update(self.variables.candidate_split_features, cleared_and_allocated_accumulators, split_features_updates))
    updates += self.finish_iteration()
    return control_flow_ops.group(*updates)