You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spark.apache.org by ur...@apache.org on 2026/08/12 09:42:52 UTC

(spark) branch branch-4.3 updated: [SPARK-58710][MLLIB] Remove unused members from the decision tree Node classes

This is an automated email from the ASF dual-hosted git repository.

uros-b pushed a commit to branch branch-4.3
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/branch-4.3 by this push:
     new fdaaaae41456 [SPARK-58710][MLLIB] Remove unused members from the decision tree Node classes
fdaaaae41456 is described below

commit fdaaaae41456bebc46139f7301bf1471cbd7228d
Author: Uros Bojanic <22...@users.noreply.github.com>
AuthorDate: Wed Aug 12 09:42:24 2026 +0000

    [SPARK-58710][MLLIB] Remove unused members from the decision tree Node classes
    
    ### What changes were proposed in this pull request?
    
    Removes members with no callers from the two decision tree `Node` files.
    
    `mllib/tree/model/Node.scala`: `deepCopy`, and the companion helpers `isLeftChild`, `maxNodesInLevel`, `startIndexInLevel`, `emptyNode`, `parentIndex`, `getNode`, `indexToLevel`.
    
    `ml/tree/Node.scala`: the `deepCopy` declaration and its two overrides, plus `parentIndex`, `isLeftChild`, `maxNodesInLevel`, `startIndexInLevel`, `getNode`.
    
    ### Why are the changes needed?
    
    None of these has a caller. `deepCopy` is reachable only from itself, so the declaration and both overrides are dead as a unit. The last callers of the indexing helpers disappeared when the decision tree implementation moved from `spark.mllib` to `spark.ml`.
    
    Both files are covered because `ml/tree/Node.scala` carries the comment `// The below indexing methods were copied from spark.mllib.tree.model.Node`, so removing only the original would leave the labelled copy behind.
    
    The live members are kept: `apply`, `leftChildIndex` and `rightChildIndex` in both objects, plus `emptyNode` and `indexToLevel` in `LearningNode`, which `RandomForest` still calls.
    
    ### Does this PR introduce _any_ user-facing change?
    
    No. The `mllib` companion object is `private[spark]` and `deepCopy` is `private[tree]`, so nothing removed is public API. `GenerateMIMAIgnore` already excludes package-private members, so no `MimaExcludes` entry is required.
    
    ### How was this patch tested?
    
    Pure removal of unreferenced code; no test references any removed member. Deadness was checked for each name across all file types, and dynamic reachability was ruled out explicitly: none appears as a string literal, there is no reflection or codegen in either tree package, and no MiMa filter names them. The two `Node` hierarchies are unrelated (`ml` imports the `mllib` one under an alias), so removing from one cannot affect the other.
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code (Opus 4.8)
    
    Closes #57928 from uros-b/deadcode-mllib-node-unused.
    
    Authored-by: Uros Bojanic <22...@users.noreply.github.com>
    Signed-off-by: Uros Bojanic <22...@users.noreply.github.com>
    (cherry picked from commit 0a9c89fb43e2cb3b0349698eadd6304f0a23a0d1)
    Signed-off-by: Uros Bojanic <22...@users.noreply.github.com>
---
 .../main/scala/org/apache/spark/ml/tree/Node.scala | 53 ----------------
 .../org/apache/spark/mllib/tree/model/Node.scala   | 73 ----------------------
 2 files changed, 126 deletions(-)

diff --git a/mllib/src/main/scala/org/apache/spark/ml/tree/Node.scala b/mllib/src/main/scala/org/apache/spark/ml/tree/Node.scala
index b68a5e079dc4..a52b79b377fa 100644
--- a/mllib/src/main/scala/org/apache/spark/ml/tree/Node.scala
+++ b/mllib/src/main/scala/org/apache/spark/ml/tree/Node.scala
@@ -77,9 +77,6 @@ sealed abstract class Node extends Serializable {
    * @return  Max feature index used in a split, or -1 if there are no splits (single leaf node).
    */
   private[ml] def maxSplitFeatureIndex(): Int
-
-  /** Returns a deep copy of the subtree rooted at this node. */
-  private[tree] def deepCopy(): Node
 }
 
 private[ml] object Node {
@@ -146,10 +143,6 @@ class LeafNode private[ml] (
   }
 
   override private[ml] def maxSplitFeatureIndex(): Int = -1
-
-  override private[tree] def deepCopy(): Node = {
-    new LeafNode(prediction, impurity, impurityStats)
-  }
 }
 
 /**
@@ -238,11 +231,6 @@ class InternalNode private[ml] (
     math.max(split.featureIndex,
       math.max(leftChild.maxSplitFeatureIndex(), rightChild.maxSplitFeatureIndex()))
   }
-
-  override private[tree] def deepCopy(): Node = {
-    new InternalNode(prediction, impurity, gain, leftChild.deepCopy(), rightChild.deepCopy(),
-      split, impurityStats)
-  }
 }
 
 private object InternalNode {
@@ -393,11 +381,6 @@ private[tree] object LearningNode {
    */
   def rightChildIndex(nodeIndex: Int): Int = (nodeIndex << 1) + 1
 
-  /**
-   * Get the parent index of the given node, or 0 if it is the root.
-   */
-  def parentIndex(nodeIndex: Int): Int = nodeIndex >> 1
-
   /**
    * Return the level of a tree which the given node is in.
    */
@@ -407,40 +390,4 @@ private[tree] object LearningNode {
     java.lang.Integer.numberOfTrailingZeros(java.lang.Integer.highestOneBit(nodeIndex))
   }
 
-  /**
-   * Returns true if this is a left child.
-   * Note: Returns false for the root.
-   */
-  def isLeftChild(nodeIndex: Int): Boolean = nodeIndex > 1 && nodeIndex % 2 == 0
-
-  /**
-   * Return the maximum number of nodes which can be in the given level of the tree.
-   * @param level  Level of tree (0 = root).
-   */
-  def maxNodesInLevel(level: Int): Int = 1 << level
-
-  /**
-   * Return the index of the first node in the given level.
-   * @param level  Level of tree (0 = root).
-   */
-  def startIndexInLevel(level: Int): Int = 1 << level
-
-  /**
-   * Traces down from a root node to get the node with the given node index.
-   * This assumes the node exists.
-   */
-  def getNode(nodeIndex: Int, rootNode: LearningNode): LearningNode = {
-    var tmpNode: LearningNode = rootNode
-    var levelsToGo = indexToLevel(nodeIndex)
-    while (levelsToGo > 0) {
-      if ((nodeIndex & (1 << levelsToGo - 1)) == 0) {
-        tmpNode = tmpNode.leftChild.get
-      } else {
-        tmpNode = tmpNode.rightChild.get
-      }
-      levelsToGo -= 1
-    }
-    tmpNode
-  }
-
 }
diff --git a/mllib/src/main/scala/org/apache/spark/mllib/tree/model/Node.scala b/mllib/src/main/scala/org/apache/spark/mllib/tree/model/Node.scala
index a0eec8b2afb9..6ac0bfdc7b63 100644
--- a/mllib/src/main/scala/org/apache/spark/mllib/tree/model/Node.scala
+++ b/mllib/src/main/scala/org/apache/spark/mllib/tree/model/Node.scala
@@ -80,23 +80,6 @@ class Node @Since("1.2.0") (
     }
   }
 
-  /**
-   * Returns a deep copy of the subtree rooted at this node.
-   */
-  private[tree] def deepCopy(): Node = {
-    val leftNodeCopy = if (leftNode.isEmpty) {
-      None
-    } else {
-      Some(leftNode.get.deepCopy())
-    }
-    val rightNodeCopy = if (rightNode.isEmpty) {
-      None
-    } else {
-      Some(rightNode.get.deepCopy())
-    }
-    new Node(id, predict, impurity, isLeaf, split, leftNodeCopy, rightNodeCopy, stats)
-  }
-
   /**
    * Get the number of nodes in tree below this node, including leaf nodes.
    * E.g., if this is a leaf, returns 0.  If both children are leaves, returns 2.
@@ -157,12 +140,6 @@ class Node @Since("1.2.0") (
 
 private[spark] object Node {
 
-  /**
-   * Return a node with the given node id (but nothing else set).
-   */
-  def emptyNode(nodeIndex: Int): Node = new Node(nodeIndex, new Predict(Double.MinValue), -1.0,
-    false, None, None, None, None)
-
   /**
    * Construct a node with nodeIndex, predict, impurity and isLeaf parameters.
    * This is used in `DecisionTree.findBestSplits` to construct child nodes
@@ -192,54 +169,4 @@ private[spark] object Node {
    */
   def rightChildIndex(nodeIndex: Int): Int = (nodeIndex << 1) + 1
 
-  /**
-   * Get the parent index of the given node, or 0 if it is the root.
-   */
-  def parentIndex(nodeIndex: Int): Int = nodeIndex >> 1
-
-  /**
-   * Return the level of a tree which the given node is in.
-   */
-  def indexToLevel(nodeIndex: Int): Int = if (nodeIndex == 0) {
-    throw new IllegalArgumentException(s"0 is not a valid node index.")
-  } else {
-    java.lang.Integer.numberOfTrailingZeros(java.lang.Integer.highestOneBit(nodeIndex))
-  }
-
-  /**
-   * Returns true if this is a left child.
-   * Note: Returns false for the root.
-   */
-  def isLeftChild(nodeIndex: Int): Boolean = nodeIndex > 1 && nodeIndex % 2 == 0
-
-  /**
-   * Return the maximum number of nodes which can be in the given level of the tree.
-   * @param level  Level of tree (0 = root).
-   */
-  def maxNodesInLevel(level: Int): Int = 1 << level
-
-  /**
-   * Return the index of the first node in the given level.
-   * @param level  Level of tree (0 = root).
-   */
-  def startIndexInLevel(level: Int): Int = 1 << level
-
-  /**
-   * Traces down from a root node to get the node with the given node index.
-   * This assumes the node exists.
-   */
-  def getNode(nodeIndex: Int, rootNode: Node): Node = {
-    var tmpNode: Node = rootNode
-    var levelsToGo = indexToLevel(nodeIndex)
-    while (levelsToGo > 0) {
-      if ((nodeIndex & (1 << levelsToGo - 1)) == 0) {
-        tmpNode = tmpNode.leftNode.get
-      } else {
-        tmpNode = tmpNode.rightNode.get
-      }
-      levelsToGo -= 1
-    }
-    tmpNode
-  }
-
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@spark.apache.org
For additional commands, e-mail: commits-help@spark.apache.org