// file: $isip/class/pr/RegressionDecisionTree/rdt_05.cc // version: $Id: rdt_05.cc 9920 2004-12-23 21:07:46Z gao $ // // isip include files // #include "RegressionDecisionTree.h" // method: initRegressionTree // // arguments: // Vector& stat_models: (input) vector of statistical model // Vector & speech_tag: (input) speech tag // // return: a bool8 value indicating status // // this method initilaize the regression tree root node using the // statistical models and speech tag // bool8 RegressionDecisionTree::initRegressionTree(Vector& stat_models_a, Vector & speech_tag_a) { // keep a copy of the model in the local data field // stat_models_d.assign(stat_models_a); // get the number of features // int32 num_feat = 0; // if the model to adapt is Gaussian model, get feature dimension // if (stat_models_a(0).getType() == StatisticalModel::GAUSSIAN_MODEL) { VectorFloat mean; stat_models_a(0).getGaussianModel().getMean(mean); num_feat = mean.length(); } // if the model to adapt is mixture model, get feature dimension of // the first mixture component // else if (stat_models_a(0).getType() == StatisticalModel::MIXTURE_MODEL) { VectorFloat mean; StatisticalModel* sm = stat_models_a(0).getMixtureModel() .getModels().getFirst(); sm->getMean(mean); num_feat = mean.length(); } else { return Error::handle(name(), L"initRegressionTree", ERR_ADAPT_NO_GAUSSIAN, __FILE__, __LINE__); } // minimal components will be three times the number of feature // components_threshold_d = 3 * num_feat; SingleLinkedList models_root; SingleLinkedList models_speech; SingleLinkedList models_nonspeech; VectorLong stat_mixture; int32 stat_length = stat_models_a.length(); stat_mixture.setLength(stat_length); mixture_offset_d.setLength(stat_length); // loop over all statistical models // for (int i = 0; i < stat_models_a.length(); i++) { // if the model to adapt is a single Gaussian model, add to data // strucutre directly // if (stat_models_a(i).getType() == StatisticalModel::GAUSSIAN_MODEL) { RDataPoint temp_point; int32 w_index = 0; temp_point.assign(i, 1, w_index); // add to the node // if (speech_tag_a(i)) models_speech.insert(&temp_point); else models_nonspeech.insert(&temp_point); models_root.insert(&temp_point); stat_mixture(i) = 1; } // if the model to adapt is a mixture of Gaussian models, add each // mixture component to the data structure // else if (stat_models_a(i).getType() == StatisticalModel::MIXTURE_MODEL) { // get the linked list of mixture components // SingleLinkedList& models = const_cast(stat_models_a(i)).getMixtureModel(). getModels(); bool8 more = models.gotoFirst(); // Gaussian Model index // int32 w_index = 0; while (more) { // get the current mixture component // StatisticalModel* model = models.getCurr(); // check whether it is Gaussian // if (model->getType() == StatisticalModel::GAUSSIAN_MODEL) { RDataPoint temp_point; temp_point.assign(i, w_index, 0); if (speech_tag_a(i)) models_speech.insert(&temp_point); else models_nonspeech.insert(&temp_point); models_root.insert(&temp_point); w_index++; } else { return Error::handle(name(), L"initRegressionTree", RegressionDecisionTree::ERR_ADAPT_NO_GAUSSIAN, __FILE__, __LINE__); } // move to the next mixture component // more = models.gotoNext(); } // end of loop over all mixtures stat_mixture(i) = w_index; } // end of else if mixture model // it is neither single Gaussian, nor mixture model // else { return Error::handle(name(), L"initRegressionTree", RegressionDecisionTree::ERR_ADAPT_NO_GAUSSIAN, __FILE__, __LINE__); } // end of else unsupported model } // end of loop over all statistical models mixture_offset_d(0) = 0; for (int32 j = 1; j < stat_length; j++) { mixture_offset_d(j) = mixture_offset_d(j - 1) + stat_mixture(j - 1); } int32 mixture_total = mixture_offset_d(stat_length - 1) + stat_mixture(stat_length - 1); map_stat_to_trans_d.setLength(mixture_total); // construct the root node and insert it in the graph // BiGraphVertex* rootnode = insertVertex(&rdt_rootnode_d); // connect the start node to the root node // insertArc(getStart(), rootnode, false, 0); // local variables // RegressionDecisionTreeNode* rdt_node = (RegressionDecisionTreeNode*)NULL; // get the data on this node // rdt_node = rootnode->getItem(); rdt_node->setComponents(models_root); rdt_node->setNodeIndex(0); rdt_node->setParentNodeIndex(0); if (debug_level_d >= Integral::ALL) { Long x_reg = stat_models_a.length(); x_reg.debug(L"x_reg"); Long(models_speech.length()).debug(L"models_speech"); Long(models_nonspeech.length()).debug(L"models_nonspeech"); } // local variables // RegressionDecisionTreeNode child_rdt_node1; RegressionDecisionTreeNode child_rdt_node2; child_rdt_node1.setComponents(models_speech); child_rdt_node1.setNodeIndex(1); child_rdt_node1.setParentNodeIndex(0); child_rdt_node1.setSpeechFlag(true); BiGraphVertex* left_node = insertVertex(&child_rdt_node1); insertArc(rootnode, left_node, true); if (speech_tag_a.length() > 0) { child_rdt_node2.setComponents(models_nonspeech); child_rdt_node2.setNodeIndex(2); child_rdt_node2.setParentNodeIndex(0); child_rdt_node2.setSpeechFlag(false); BiGraphVertex* right_node = insertVertex(&child_rdt_node2); insertArc(rootnode, right_node, true); speech_flag_d = true; } // build the regression cluster // buildRegressionCluster(rootnode); RTreeNode* root_node = (RTreeNode*)NULL; SingleLinkedList leaf_nodes(DstrBase::USER); root_node = getFirst(); leaf_nodes.clear(); getAllLeafNodes(*root_node, leaf_nodes); if (debug_level_d >= Integral::ALL) { for (bool8 more = leaf_nodes.gotoFirst(); more; more = leaf_nodes.gotoNext()) { leaf_nodes.getCurr()->getItem()->getNodeIndex().debug(L"leaf index2"); } } // exit gracefully // return true; } // method: findBestTerminal // // arguments: // TreeNode*& node: (input) input node // TreeNode*& best: (input) best node to split // float32& score: (input/output) the best score upto now // // return: a TreeNode point for the best node // // this method return the best node to split when the input root node // is given by using the best score // BiGraphVertex * RegressionDecisionTree::findBestTerminal(RTreeNode*& node_a, RTreeNode*& best_a, float32& score_a) { // local variables // float32 score = (float32)0; DoubleLinkedList >* children; BiGraphVertex* child_node; // check the node // if (node_a != (RTreeNode*)NULL) { // get all the child nodes of this node // children = node_a->getChildren(); // if this node has child nodes, accumulate the child nodes // if (node_a->gotoFirstChild()) { // loop for the left child // children->gotoFirst(); child_node = children->getCurr()->getVertex(); best_a = findBestTerminal(child_node, best_a, score_a); } else { // local variables // RegressionDecisionTreeNode* rdt_node = (RegressionDecisionTreeNode*)NULL; // get the data on this node // rdt_node = node_a->getItem(); rdt_node->updateDistribution(stat_models_d); score = rdt_node->getClusterScore(); int32 num_component = rdt_node->getNumComponents(); // if the node's score is greater, record the score and set the // best node to this node // if (score_a < score && num_component > components_threshold_d) { score_a = score; best_a = node_a; } return best_a; } if( children->gotoNext()) { // loop for the right child // child_node = children->getCurr()->getVertex(); best_a = findBestTerminal(child_node, best_a, score_a); } } // return the best node // return best_a; } // method: buildRegressionClusters // // arguments: // (RTreeNode*& root_node: (input) root node // // return: a bool8 value indicating status // // this method creates (train) the decision-tree on the basis of // algorithm and implementation and the root node // bool8 RegressionDecisionTree::buildRegressionCluster(RTreeNode*& root_node_a) { int32 k, index; float32 score; int32 components; VectorFloat average_mean; VectorFloat left_average_mean, right_average_mean; MatrixFloat average_covariance; // local variables // RegressionDecisionTreeNode* left_child_node; RegressionDecisionTreeNode* right_child_node; left_child_node = new RegressionDecisionTreeNode(); right_child_node = new RegressionDecisionTreeNode(); RTreeNode* best_node; // the root includes all the data, if first split the non-speech and // speech data. This will create 3 nodes, index will be 0, 1, 2 // // So the next split index starts from 3. // // if no split, the index start from 1; if (speech_flag_d) // exculde non speech symbol index = 3; else index = 1; // include non speech symbol k = index / 2; for (; k < num_terminals_d; k++) { score = 0.0; components = 0; best_node = root_node_a; // find the best (worst scoring) terminal to split // based on the score and number of the components // best_node = findBestTerminal(root_node_a, best_node, score); //best_node = findBestTerminal(root_node_a, best_node, components); if (debug_level_d >= Integral::ALL) { best_node->getItem()->getNodeIndex().debug(L"split node"); } // check to see if there are no more nodes to split // if ((k > 1 || score == 0.0) && best_node == root_node_a) { break; } // local variables // RegressionDecisionTreeNode* rdt_node = (RegressionDecisionTreeNode*)NULL; // get the data on this node // rdt_node = best_node->getItem(); rdt_node->updateDistribution(stat_models_d); // get the parent node distribution // such as average mean and average covar // rdt_node->getAverageMean(average_mean); rdt_node->getAverageCov(average_covariance); average_mean.debug(L"average_mean--best node point"); average_covariance.debug(L"average_covariance--first check point"); // copy parent node distribution to children // left_child_node->setAverageMean(average_mean); right_child_node->setAverageMean(average_mean); left_child_node->setAverageCov(average_covariance); right_child_node->setAverageCov(average_covariance); // perturb mean of the children // perturbMean(left_child_node, 0.05); perturbMean(right_child_node, -0.05); left_child_node->getAverageMean(left_average_mean); right_child_node->getAverageMean(right_average_mean); // cluster node information to childre // clusterChildren(rdt_node, left_child_node, right_child_node); if ((int32)left_child_node->getNumComponents() == 0 || (int32)right_child_node->getNumComponents() == 0) { best_node->getItem()->setSplitFlag(false); k--; // ignore this loop and select another node to split continue; } int32 parent_index = best_node->getItem()->getNodeIndex(); left_child_node->setParentNodeIndex(parent_index); right_child_node->setParentNodeIndex(parent_index); left_child_node->setNodeIndex(index++); right_child_node->setNodeIndex(index++); // create new children to link to // BiGraphVertex* left_node = insertVertex(left_child_node); BiGraphVertex* right_node = insertVertex(right_child_node); insertArc(best_node, left_node, true); insertArc(best_node, right_node, true); } // exit gracefully // return true; } // method: perturbMean // // arguments: // RegressionDecisionTreeNode*& child_node: (input) node to perturb the mean // float64 perturb_depth: (input) perturb depth // // return: a bool8 value indicating status // // this method perturb mean using given perturb depth // bool8 RegressionDecisionTree::perturbMean(RegressionDecisionTreeNode*& child_node_a, float64 perturb_depth_a) { VectorFloat mean; MatrixFloat covariance; child_node_a->getAverageMean(mean); child_node_a->getAverageCov(covariance); for (int32 i = 0; i < mean.length(); i++) { float64 x = perturb_depth_a * covariance(i, i); mean(i) += (float32)x; } child_node_a->setAverageMean(mean); // exit gracefully // return true; } // method: clusterChildren // // arguments: // RegressionDecisionTreeNode*& root_node: (input) parent node // RegressionDecisionTreeNode*& left_child_node: (input) left child node // RegressionDecisionTreeNode*& right_child_node: (input) right child node // // return: a bool8 value indicating status // // this method cluster the components in the parent node to its left // and right child nodes // bool8 RegressionDecisionTree::clusterChildren(RegressionDecisionTreeNode*& root_node_a, RegressionDecisionTreeNode*& left_child_node_a, RegressionDecisionTreeNode*& right_child_node_a) { const float64 threshold = 1.0e-12; int32 iteration = 0; float64 oldDistance, newDistance = 0.0; int32 MAX_ITERATION = 10000; do { iteration += 1; if (iteration >= MAX_ITERATION) break; calculateDistance(root_node_a, left_child_node_a, right_child_node_a); if (iteration == 1) oldDistance = left_child_node_a->getClusterScore() + right_child_node_a->getClusterScore() + threshold; else oldDistance = newDistance; newDistance = (left_child_node_a->getClusterScore() * left_child_node_a->getClusterAccumulate() + right_child_node_a->getClusterScore() * right_child_node_a->getClusterAccumulate()) / (left_child_node_a->getClusterAccumulate() + right_child_node_a->getClusterAccumulate()); } while ((oldDistance - newDistance) > threshold); createChildNode(root_node_a, left_child_node_a, right_child_node_a); // exit gracefully // return true; } // method: calculateDistance // // arguments: // RegressionDecisionTreeNode*& root_node: (input) parent node // RegressionDecisionTreeNode*& left_child_node: (input) left child node // RegressionDecisionTreeNode*& right_child_node: (input) right child node // // return: a bool8 value indicating status // // this method calculates the distance for all the components with // respect to its average mean // bool8 RegressionDecisionTree::calculateDistance(RegressionDecisionTreeNode*& root_node_a, RegressionDecisionTreeNode*& left_child_node_a, RegressionDecisionTreeNode*& right_child_node_a) { float64 score1, score2; VectorFloat mean; VectorFloat left_mean_sum; VectorFloat right_mean_sum; VectorFloat left_average_mean, right_average_mean; Double occ; float64 left_accu = 0.0, right_accu = 0.0; float64 left_score = 0.0, right_score = 0.0; int32 temp_left_index = 0, temp_right_index = 0; left_child_node_a->getAverageMean(left_average_mean); right_child_node_a->getAverageMean(right_average_mean); // loop all the models in the root node and calculate the mean value // under the current classification condition // RData gaussian_models; gaussian_models = root_node_a->getComponents(); // loop over all gaussian model index // for (bool8 more_index = gaussian_models.gotoFirst(); more_index; more_index = gaussian_models.gotoNext()) { // get the first datapoint in triple // RDataPoint* datapoint = gaussian_models.getCurr(); // get the statistical model index // int32 sm_index = (int32)datapoint->first(); // get the gaussian model index // int32 gm_index = (int32)datapoint->second(); // if the model to adapt is a single Gaussian model, compute its // contribution to the global adaptation // if (stat_models_d(sm_index).getType() == StatisticalModel::GAUSSIAN_MODEL) { GaussianModel& gm = stat_models_d(sm_index).getGaussianModel(); // compute the contribution of this Gaussian model to the global // adaptation // gm.getMean(mean); occ = gm.getOccupancy(); } // if the model to adapt is a mixture of Gaussian models, compute // the contribution of mixture component match the index to the // global adaptation // else if (stat_models_d(sm_index).getType() == StatisticalModel::MIXTURE_MODEL) { // get the linked list of mixture components // SingleLinkedList& models = const_cast(stat_models_d(sm_index)). getMixtureModel().getModels(); // check whether each mixture component is Gaussian model and // compute its contribution to the global adaptation // bool8 more = models.gotoFirst(); int32 local_gaussian_index = 0; while (more) { // get the current mixture component // StatisticalModel* model = models.getCurr(); // check whether it is Gaussian // if (model->getType() == StatisticalModel::GAUSSIAN_MODEL) { GaussianModel& gm = model->getGaussianModel(); // compute the contribution of this Gaussian model to the // global adaptation // if (local_gaussian_index == gm_index) { gm.getMean(mean); occ = gm.getOccupancy(); break; } else local_gaussian_index++; } else { return Error::handle(name(), L"calculateDistance", RegressionDecisionTree::ERR_ADAPT_NO_GAUSSIAN, __FILE__, __LINE__); } // move to the next mixture component // more = models.gotoNext(); } // end of loop over all mixtures } // end of else if mixture model // Calculate the distance between this mean and average mean of // each child node score1 = mean.distance(left_average_mean); score2 = mean.distance(right_average_mean); VectorFloat temp_mean; temp_mean.assign(mean); temp_mean.mult(occ); if (score1 < score2) { // accumulate the occupancy count to left node // left_accu += occ; left_score += (occ*score1); if (temp_left_index == 0) { left_mean_sum.assign(temp_mean); temp_left_index++; } else { left_mean_sum.add(temp_mean); temp_left_index++; } } else { // accumulate the occupancy count to the right node // right_accu += occ; right_score += (occ*score2); if (temp_right_index == 0) { right_mean_sum.assign(temp_mean); temp_right_index++; } else { temp_right_index++; right_mean_sum.add(temp_mean); } } } // end of all model loop // recalculate the average mean for each node // left_average_mean.assign(left_mean_sum); right_average_mean.assign(right_mean_sum); left_average_mean.div(left_accu); right_average_mean.div(right_accu); left_child_node_a->setAverageMean(left_average_mean); right_child_node_a->setAverageMean(right_average_mean); left_child_node_a->setClusterAccumulate(left_accu); right_child_node_a->setClusterAccumulate(right_accu); left_child_node_a->setClusterScore(left_score); right_child_node_a->setClusterScore(right_score); left_child_node_a->setNumComponents(temp_left_index); right_child_node_a->setNumComponents(temp_right_index); // exit gracefully // return true; } // method: createChildNodes // // arguments: // RegressionDecisionTreeNode*& root_node: (input) parent node // RegressionDecisionTreeNode*& left_child_node: (input) left child node // RegressionDecisionTreeNode*& right_child_node: (input) right child node // // return: a bool8 value indicating status // // this method classifies the components in parent node to its child // nodes // bool8 RegressionDecisionTree::createChildNode(RegressionDecisionTreeNode*& root_node_a, RegressionDecisionTreeNode*& left_child_node_a, RegressionDecisionTreeNode*& right_child_node_a) { float64 score_left, score_right; int32 num_left = 0, num_right = 0; VectorFloat mean, left_average_mean, right_average_mean; RData left, right; // loop all the models in the root node and calculate the mean value // under the current classification condition // RData gaussian_models; gaussian_models = root_node_a->getComponents(); if ((int32)left_child_node_a->getNumComponents() == 0 || (int32)right_child_node_a->getNumComponents() == 0) { return false; } // loop over all gaussian model index // for (bool8 more_index = gaussian_models.gotoFirst(); more_index; more_index = gaussian_models.gotoNext()) { // get the first datapoint in triple // RDataPoint* datapoint = gaussian_models.getCurr(); // get the statistical model index // int32 sm_index = (int32)datapoint->first(); // get the gaussian model index // int32 gm_index = (int32)datapoint->second(); GaussianModel gm; getGaussianModel(sm_index, gm_index, gm); gm.getMean(mean); left_child_node_a->getAverageMean(left_average_mean); right_child_node_a->getAverageMean(right_average_mean); // Calculate the distance between this mean and average mean of // each child node score_left = mean.distance(left_average_mean); score_right = mean.distance(right_average_mean); if (score_left < score_right) { // insert to left node // left.insert(datapoint); num_left++; } else { // insert to right node // right.insert(datapoint); num_right++; } } // end of all model loop if (num_left == 0 || num_right == 0) { return false; } left_child_node_a->setNumComponents(num_left); right_child_node_a->setNumComponents(num_right); left_child_node_a->setComponents(left); right_child_node_a->setComponents(right); // calculate the average mean and variance of the cluster // left_child_node_a->updateDistribution(stat_models_d); right_child_node_a->updateDistribution(stat_models_d); // exit gracefully // return true; } // method: runDecisionTree // // arguments: // none // // return: a bool8 value indicating status // // this method runs the decisiontree using the specified runmode and // stopmode. // bool8 RegressionDecisionTree::runDecisionTree() { // exit gracefully // return true; } // method: getMean // // arguments: // int32 sm_index: (input) statistical model index // int32 gm_index: (input) Gaussian model index // VectorFloat& mean: (output) mean value for this model specified // by the index of the statistical model index // and Gaussian model index // // return: a bool8 value indicating status // // this method get the mean value for the specified modle using the // index for statistical model and Gaussian model // bool8 RegressionDecisionTree::getMean(int32 sm_index_a, int32 gm_index_a, VectorFloat& mean_a) { // if the model to adapt is a single Gaussian model, directly get // the mean // if (stat_models_d(sm_index_a).getType() == StatisticalModel::GAUSSIAN_MODEL) { GaussianModel& gm = stat_models_d(sm_index_a).getGaussianModel(); // get the mean value of this model // gm.getMean(mean_a); } // if the model to adapt is a mixture of Gaussian models, compute // the contribution of mixture component match the index to the // mean // else if (stat_models_d(sm_index_a).getType() == StatisticalModel::MIXTURE_MODEL) { // get the linked list of mixture components // SingleLinkedList& models = const_cast(stat_models_d(sm_index_a)). getMixtureModel().getModels(); // check whether each mixture component is Gaussian model and // compute its contribution to the global adaptation // bool8 more = models.gotoFirst(); int32 local_gaussian_index = 0; while (more) { // get the current mixture component // StatisticalModel* model = models.getCurr(); // check whether it is Gaussian // if (model->getType() == StatisticalModel::GAUSSIAN_MODEL) { GaussianModel& gm = model->getGaussianModel(); //Long(gm.getAccessCount()).debug(L"access count in createTrans is"); // compute the contribution of this Gaussian model to the // global adaptation // if (local_gaussian_index == gm_index_a) { gm.getMean(mean_a); break; } else local_gaussian_index++; } else { return Error::handle(name(), L"getMean", RegressionDecisionTree::ERR_ADAPT_NO_GAUSSIAN, __FILE__, __LINE__); } // move to the next mixture component // more = models.gotoNext(); } // end of loop over all mixtures } // end of else if mixture model // exit gracefully // return true; } // method: getGaussianModel // // arguments // int32 sm_index: (input) statistical model index // int32 gm_index: (input) Gaussian model index // GaussianModel& gm: (output) Gaussian model specified // by the index of the statistical model index // and Gaussian model index // // return: a bool8 value indicating status // // this method get the Gaussian model by specified model using the // index for statistical model and Gaussian model // bool8 RegressionDecisionTree::getGaussianModel(int32 sm_index_a, int32 gm_index_a, GaussianModel& gm_a) { // local variable // bool8 res = false; // if the model is a single Gaussian model, get the model directly // if (stat_models_d(sm_index_a).getType() == StatisticalModel::GAUSSIAN_MODEL) { gm_a = stat_models_d(sm_index_a).getGaussianModel(); res = true; } // if the model is a mixture of Gaussian models, loop to find the // model match the index // else if (stat_models_d(sm_index_a).getType() == StatisticalModel::MIXTURE_MODEL) { // get the linked list of mixture components // SingleLinkedList& models = const_cast(stat_models_d(sm_index_a)). getMixtureModel().getModels(); // check whether each mixture component is Gaussian model and // compute its contribution to the global adaptation // bool8 more = models.gotoFirst(); int32 local_gaussian_index = 0; while (more) { // get the current mixture component // StatisticalModel* model = models.getCurr(); // check whether it is Gaussian // if (model->getType() == StatisticalModel::GAUSSIAN_MODEL) { // compute the contribution of this Gaussian model to the // global adaptation // if (local_gaussian_index == gm_index_a) { gm_a = model->getGaussianModel(); res = true; break; } else local_gaussian_index++; } else { return Error::handle(name(), L"getGaussianModel", RegressionDecisionTree::ERR_ADAPT_NO_GAUSSIAN, __FILE__, __LINE__); } // move to the next mixture component // more = models.gotoNext(); } // end of loop over all mixtures } // end of else if mixture model // exit gracefully // return res; } // method: findBestTerminal // // arguments: // TreeNode* node: (input) input node // TreeNode* best: (input) best node to split // int32& component: (input/output) the maximum components upto now // // return: a TreeNode point for the best node // // this method return the best node to split when the input root node // is given by using the maximum components // BiGraphVertex* RegressionDecisionTree::findBestTerminal(RTreeNode*& node_a, RTreeNode*& best_a, int32& components_a) { // local variables // int32 component = 0; DoubleLinkedList< BiGraphArc >* children; BiGraphVertex* child_node; // check the node // if (node_a != (RTreeNode*)NULL) { // get all the child nodes of this node // children = node_a->getChildren(); // if this node has child nodes, accumulate the child nodes // if (node_a->gotoFirstChild()) { // loop for the left child // children->gotoFirst(); child_node = children->getCurr()->getVertex(); best_a = findBestTerminal(child_node, best_a, components_a); } else { // local variables // RegressionDecisionTreeNode* rdt_node = (RegressionDecisionTreeNode*)NULL; // get the data on this node // rdt_node = node_a->getItem(); rdt_node->updateDistribution(stat_models_d); // if this node is unsplitable, return the previous best node // if (!(rdt_node -> getSplitFlag())) { return best_a; } component = rdt_node->getNumComponents(); // if the node's score is greater, record the score and set the // best node to this node // if (components_a < component && component > components_threshold_d) { components_a = component; best_a = node_a; } return best_a; } if( children->gotoNext()) { // loop for the right child // child_node = children->getCurr()->getVertex(); best_a = findBestTerminal(child_node, best_a, components_a); } } // return the best node // return best_a; }