StochTree 0.5.0.9000
Loading...
Searching...
No Matches
tree_sampler.h
1
2#ifndef STOCHTREE_TREE_SAMPLER_H_
3#define STOCHTREE_TREE_SAMPLER_H_
4
5#include <stochtree/container.h>
6#include <stochtree/cutpoint_candidates.h>
7#include <stochtree/data.h>
8#include <stochtree/discrete_sampler.h>
9#include <stochtree/distributions.h>
10#include <stochtree/ensemble.h>
11#include <stochtree/leaf_model.h>
12#include <stochtree/openmp_utils.h>
13#include <stochtree/partition_tracker.h>
14#include <stochtree/prior.h>
15
16#include <cmath>
17#include <numeric>
18#include <random>
19#include <vector>
20
21namespace StochTree {
22
49 var_min = std::numeric_limits<double>::max();
50 var_max = std::numeric_limits<double>::min();
51 double feature_value;
52
53 std::vector<data_size_t>::iterator node_begin_iter = tracker.UnsortedNodeBeginIterator(tree_num, leaf_split);
54 std::vector<data_size_t>::iterator node_end_iter = tracker.UnsortedNodeEndIterator(tree_num, leaf_split);
55
56 for (auto i = node_begin_iter; i != node_end_iter; i++) {
57 auto idx = *i;
58 feature_value = dataset.CovariateValue(idx, feature_split);
59 // Compute var_min and var_max for the feature being split on, which are used to determine the range of valid cutpoints for this split
60 if (feature_value < var_min) {
62 }
63 if (feature_value > var_max) {
65 }
66 }
67}
68
81 int p = dataset.GetCovariates().cols();
82 data_size_t idx;
83 double feature_value;
85 double var_max_left;
86 double var_min_left;
87 double var_max_right;
88 double var_min_right;
89
90 for (int j = 0; j < p; j++) {
91 auto node_begin_iter = tracker.UnsortedNodeBeginIterator(tree_num, leaf_split);
92 auto node_end_iter = tracker.UnsortedNodeEndIterator(tree_num, leaf_split);
93 var_max_left = std::numeric_limits<double>::min();
94 var_min_left = std::numeric_limits<double>::max();
95 var_max_right = std::numeric_limits<double>::min();
96 var_min_right = std::numeric_limits<double>::max();
97
98 for (auto i = node_begin_iter; i != node_end_iter; i++) {
99 auto idx = *i;
100 split_feature_value = dataset.CovariateValue(idx, feature_split);
101 feature_value = dataset.CovariateValue(idx, j);
102 if (split.SplitTrue(split_feature_value)) {
105 } else if (var_min_left > feature_value) {
107 }
108 } else {
111 } else if (var_min_right > feature_value) {
113 }
114 }
115 }
117 return true;
118 }
119 }
120 return false;
121}
122
123static inline bool NodeNonConstant(ForestDataset& dataset, ForestTracker& tracker, int tree_num, int node_id) {
124 int p = dataset.GetCovariates().cols();
125 data_size_t idx;
126 double feature_value;
127 double var_max;
128 double var_min;
129
130 for (int j = 0; j < p; j++) {
131 auto node_begin_iter = tracker.UnsortedNodeBeginIterator(tree_num, node_id);
132 auto node_end_iter = tracker.UnsortedNodeEndIterator(tree_num, node_id);
133 var_max = std::numeric_limits<double>::min();
134 var_min = std::numeric_limits<double>::max();
135
136 for (auto i = node_begin_iter; i != node_end_iter; i++) {
137 auto idx = *i;
138 feature_value = dataset.CovariateValue(idx, j);
139 if (var_max < feature_value) {
141 } else if (var_min > feature_value) {
143 }
144 }
145 if (var_max > var_min) {
146 return true;
147 }
148 }
149 return false;
150}
151
152static inline void AddSplitToModel(ForestTracker& tracker, ForestDataset& dataset, TreePrior& tree_prior, TreeSplit& split, std::mt19937& gen, Tree* tree,
153 int tree_num, int leaf_node, int feature_split, bool keep_sorted = false, int num_threads = -1) {
154 // Use zeros as a "temporary" leaf values since we draw leaf parameters after tree sampling is complete
155 if (tree->OutputDimension() > 1) {
156 std::vector<double> temp_leaf_values(tree->OutputDimension(), 0.);
158 } else {
159 double temp_leaf_value = 0.;
161 }
162 int left_node = tree->LeftChild(leaf_node);
163 int right_node = tree->RightChild(leaf_node);
164
165 // Update the ForestTracker
166 tracker.AddSplit(dataset.GetCovariates(), split, feature_split, tree_num, leaf_node, left_node, right_node, keep_sorted, num_threads);
167}
168
169static inline void RemoveSplitFromModel(ForestTracker& tracker, ForestDataset& dataset, TreePrior& tree_prior, std::mt19937& gen, Tree* tree,
170 int tree_num, int leaf_node, int left_node, int right_node, bool keep_sorted = false) {
171 // Use zeros as a "temporary" leaf values since we draw leaf parameters after tree sampling is complete
172 if (tree->OutputDimension() > 1) {
173 std::vector<double> temp_leaf_values(tree->OutputDimension(), 0.);
174 tree->CollapseToLeaf(leaf_node, temp_leaf_values);
175 } else {
176 double temp_leaf_value = 0.;
177 tree->CollapseToLeaf(leaf_node, temp_leaf_value);
178 }
179
180 // Update the ForestTracker
181 tracker.RemoveSplit(dataset.GetCovariates(), tree, tree_num, leaf_node, left_node, right_node, keep_sorted);
182}
183
184static inline double ComputeMeanOutcome(ColumnVector& residual) {
185 int n = residual.NumRows();
186 double sum_y = 0.;
187 double y;
188 for (data_size_t i = 0; i < n; i++) {
189 y = residual.GetElement(i);
190 sum_y += y;
191 }
192 return sum_y / static_cast<double>(n);
193}
194
195static inline double ComputeVarianceOutcome(ColumnVector& residual) {
196 int n = residual.NumRows();
197 double sum_y = 0.;
198 double sum_y_sq = 0.;
199 double y;
200 for (data_size_t i = 0; i < n; i++) {
201 y = residual.GetElement(i);
202 sum_y += y;
203 sum_y_sq += y * y;
204 }
205 return sum_y_sq / static_cast<double>(n) - (sum_y * sum_y) / (static_cast<double>(n) * static_cast<double>(n));
206}
207
208static inline void UpdateModelVarianceForest(ForestTracker& tracker, ForestDataset& dataset, ColumnVector& residual,
209 TreeEnsemble* forest, bool requires_basis, std::function<double(double, double)> op) {
210 data_size_t n = dataset.GetCovariates().rows();
211 double tree_pred = 0.;
212 double pred_value = 0.;
213 double new_resid = 0.;
215 for (data_size_t i = 0; i < n; i++) {
216 for (int j = 0; j < forest->NumTrees(); j++) {
217 Tree* tree = forest->GetTree(j);
218 leaf_pred = tracker.GetNodeId(i, j);
219 if (requires_basis) {
220 tree_pred += tree->PredictFromNode(leaf_pred, dataset.GetBasis(), i);
221 } else {
222 tree_pred += tree->PredictFromNode(leaf_pred);
223 }
224 tracker.SetTreeSamplePrediction(i, j, tree_pred);
226 }
227
228 // Run op (either plus or minus) on the residual and the new prediction
229 new_resid = op(residual.GetElement(i), pred_value);
230 residual.SetElement(i, new_resid);
231 }
232 tracker.SyncPredictions();
233}
234
235static inline void UpdateResidualNoTrackerUpdate(ForestTracker& tracker, ForestDataset& dataset, ColumnVector& residual, TreeEnsemble* forest,
236 bool requires_basis, std::function<double(double, double)> op) {
237 data_size_t n = dataset.GetCovariates().rows();
238 double tree_pred = 0.;
239 double pred_value = 0.;
240 double new_resid = 0.;
242 for (data_size_t i = 0; i < n; i++) {
243 for (int j = 0; j < forest->NumTrees(); j++) {
244 Tree* tree = forest->GetTree(j);
245 leaf_pred = tracker.GetNodeId(i, j);
246 if (requires_basis) {
247 tree_pred += tree->PredictFromNode(leaf_pred, dataset.GetBasis(), i);
248 } else {
249 tree_pred += tree->PredictFromNode(leaf_pred);
250 }
252 }
253
254 // Run op (either plus or minus) on the residual and the new prediction
255 new_resid = op(residual.GetElement(i), pred_value);
256 residual.SetElement(i, new_resid);
257 }
258}
259
260static inline void UpdateResidualEntireForest(ForestTracker& tracker, ForestDataset& dataset, ColumnVector& residual, TreeEnsemble* forest,
261 bool requires_basis, std::function<double(double, double)> op) {
262 data_size_t n = dataset.GetCovariates().rows();
263 double tree_pred = 0.;
264 double pred_value = 0.;
265 double new_resid = 0.;
267 for (data_size_t i = 0; i < n; i++) {
268 for (int j = 0; j < forest->NumTrees(); j++) {
269 Tree* tree = forest->GetTree(j);
270 leaf_pred = tracker.GetNodeId(i, j);
271 if (requires_basis) {
272 tree_pred += tree->PredictFromNode(leaf_pred, dataset.GetBasis(), i);
273 } else {
274 tree_pred += tree->PredictFromNode(leaf_pred);
275 }
276 tracker.SetTreeSamplePrediction(i, j, tree_pred);
278 }
279
280 // Run op (either plus or minus) on the residual and the new prediction
281 new_resid = op(residual.GetElement(i), pred_value);
282 residual.SetElement(i, new_resid);
283 }
284 tracker.SyncPredictions();
285}
286
287static inline void UpdateResidualNewOutcome(ForestTracker& tracker, ColumnVector& residual) {
288 data_size_t n = residual.NumRows();
289 double pred_value;
290 double prev_outcome;
291 double new_resid;
292 for (data_size_t i = 0; i < n; i++) {
293 prev_outcome = residual.GetElement(i);
294 pred_value = tracker.GetSamplePrediction(i);
295 // Run op (either plus or minus) on the residual and the new prediction
297 residual.SetElement(i, new_resid);
298 }
299}
300
301static inline void UpdateMeanModelTree(ForestTracker& tracker, ForestDataset& dataset, ColumnVector& residual, Tree* tree, int tree_num,
302 bool requires_basis, std::function<double(double, double)> op, bool tree_new) {
303 data_size_t n = dataset.GetCovariates().rows();
304 double pred_value;
306 double new_resid;
307 double pred_delta;
308 for (data_size_t i = 0; i < n; i++) {
309 if (tree_new) {
310 // If the tree has been newly sampled or adjusted, we must rerun the prediction
311 // method and update the SamplePredMapper stored in tracker
312 leaf_pred = tracker.GetNodeId(i, tree_num);
313 if (requires_basis) {
314 pred_value = tree->PredictFromNode(leaf_pred, dataset.GetBasis(), i);
315 } else {
316 pred_value = tree->PredictFromNode(leaf_pred);
317 }
318 pred_delta = pred_value - tracker.GetTreeSamplePrediction(i, tree_num);
319 tracker.SetTreeSamplePrediction(i, tree_num, pred_value);
320 tracker.SetSamplePrediction(i, tracker.GetSamplePrediction(i) + pred_delta);
321 } else {
322 // If the tree has not yet been modified via a sampling step,
323 // we can query its prediction directly from the SamplePredMapper stored in tracker
324 pred_value = tracker.GetTreeSamplePrediction(i, tree_num);
325 }
326 // Run op (either plus or minus) on the residual and the new prediction
327 new_resid = op(residual.GetElement(i), pred_value);
328 residual.SetElement(i, new_resid);
329 }
330}
331
332static inline void UpdateResidualNewBasis(ForestTracker& tracker, ForestDataset& dataset, ColumnVector& residual, TreeEnsemble* forest) {
333 CHECK(dataset.HasBasis());
334 data_size_t n = dataset.GetCovariates().rows();
335 int num_trees = forest->NumTrees();
336 double prev_tree_pred;
337 double new_tree_pred;
339 double new_resid;
340 for (int tree_num = 0; tree_num < num_trees; tree_num++) {
341 Tree* tree = forest->GetTree(tree_num);
342 for (data_size_t i = 0; i < n; i++) {
343 // Add back the currently stored tree prediction
344 prev_tree_pred = tracker.GetTreeSamplePrediction(i, tree_num);
345 new_resid = residual.GetElement(i) + prev_tree_pred;
346
347 // Compute new prediction based on updated basis
348 leaf_pred = tracker.GetNodeId(i, tree_num);
349 new_tree_pred = tree->PredictFromNode(leaf_pred, dataset.GetBasis(), i);
350
351 // Cache the new prediction in the tracker
352 tracker.SetTreeSamplePrediction(i, tree_num, new_tree_pred);
353
354 // Subtract out the updated tree prediction
356
357 // Propagate the change back to the residual
358 residual.SetElement(i, new_resid);
359 }
360 }
361 tracker.SyncPredictions();
362}
363
364static inline void UpdateVarModelTree(ForestTracker& tracker, ForestDataset& dataset, ColumnVector& residual, Tree* tree,
365 int tree_num, bool requires_basis, std::function<double(double, double)> op, bool tree_new) {
366 data_size_t n = dataset.GetCovariates().rows();
367 double pred_value;
369 double new_weight;
370 double pred_delta;
371 double prev_tree_pred;
372 double prev_pred;
373 for (data_size_t i = 0; i < n; i++) {
374 if (tree_new) {
375 // If the tree has been newly sampled or adjusted, we must rerun the prediction
376 // method and update the SamplePredMapper stored in tracker
377 leaf_pred = tracker.GetNodeId(i, tree_num);
378 if (requires_basis) {
379 pred_value = tree->PredictFromNode(leaf_pred, dataset.GetBasis(), i);
380 } else {
381 pred_value = tree->PredictFromNode(leaf_pred);
382 }
383 prev_tree_pred = tracker.GetTreeSamplePrediction(i, tree_num);
384 prev_pred = tracker.GetSamplePrediction(i);
386 tracker.SetTreeSamplePrediction(i, tree_num, pred_value);
387 tracker.SetSamplePrediction(i, prev_pred + pred_delta);
388 new_weight = std::log(dataset.VarWeightValue(i)) + pred_value;
389 dataset.SetVarWeightValue(i, new_weight, true);
390 } else {
391 // If the tree has not yet been modified via a sampling step,
392 // we can query its prediction directly from the SamplePredMapper stored in tracker
393 pred_value = tracker.GetTreeSamplePrediction(i, tree_num);
394 new_weight = std::log(dataset.VarWeightValue(i)) - pred_value;
395 dataset.SetVarWeightValue(i, new_weight, true);
396 }
397 }
398}
399
400static inline void UpdateCLogLogModelTree(ForestTracker& tracker, ForestDataset& dataset, ColumnVector& residual, Tree* tree, int tree_num,
401 bool requires_basis, bool tree_new) {
402 data_size_t n = dataset.GetCovariates().rows();
403
404 double pred_value;
406 double pred_delta;
407 for (data_size_t i = 0; i < n; i++) {
408 if (tree_new) {
409 // If the tree has been newly sampled or adjusted, we must rerun the prediction
410 // method and update the SamplePredMapper stored in tracker
411 leaf_pred = tracker.GetNodeId(i, tree_num);
412 if (requires_basis) {
413 pred_value = tree->PredictFromNode(leaf_pred, dataset.GetBasis(), i);
414 } else {
415 pred_value = tree->PredictFromNode(leaf_pred);
416 }
417 pred_delta = pred_value - tracker.GetTreeSamplePrediction(i, tree_num);
418 tracker.SetTreeSamplePrediction(i, tree_num, pred_value);
419 tracker.SetSamplePrediction(i, tracker.GetSamplePrediction(i) + pred_delta);
420 // Set auxiliary data slot 1 to forest predictions excluding the current tree (tree_num)
421 dataset.SetAuxiliaryDataValue(1, i, tracker.GetSamplePrediction(i) - pred_value);
422 } else {
423 // If the tree has not yet been modified via a sampling step,
424 // we can query its prediction directly from the SamplePredMapper stored in tracker
425 pred_value = tracker.GetTreeSamplePrediction(i, tree_num);
426 // Set auxiliary data slot 1 to forest predictions excluding the current tree (tree_num): needed? since tree not changed?
427 double current_lambda_hat = tracker.GetSamplePrediction(i);
429 dataset.SetAuxiliaryDataValue(1, i, lambda_minus);
430 }
431 }
432}
433
434template <typename LeafModel, typename LeafSuffStat, typename... LeafSuffStatConstructorArgs>
435static inline std::tuple<double, double, data_size_t, data_size_t> EvaluateProposedSplit(
436 ForestDataset& dataset, ForestTracker& tracker, ColumnVector& residual, LeafModel& leaf_model,
437 TreeSplit& split, int tree_num, int leaf_num, int split_feature, double global_variance,
439 // Initialize sufficient statistics
443
444 // Accumulate sufficient statistics
445 AccumulateSuffStatProposed<LeafSuffStat, LeafSuffStatConstructorArgs...>(
447 residual, global_variance, split, tree_num, leaf_num, split_feature, num_threads,
451
452 // Evaluate split
453 double split_log_ml = leaf_model.SplitLogMarginalLikelihood(left_suff_stat, right_suff_stat, global_variance);
454 double no_split_log_ml = leaf_model.NoSplitLogMarginalLikelihood(node_suff_stat, global_variance);
455
456 return std::tuple<double, double, data_size_t, data_size_t>(split_log_ml, no_split_log_ml, left_n, right_n);
457}
458
459template <typename LeafModel, typename LeafSuffStat, typename... LeafSuffStatConstructorArgs>
460static inline std::tuple<double, double, data_size_t, data_size_t> EvaluateExistingSplit(
461 ForestDataset& dataset, ForestTracker& tracker, ColumnVector& residual, LeafModel& leaf_model,
464 // Initialize sufficient statistics
468
469 // Accumulate sufficient statistics
474
475 // Evaluate split
476 double split_log_ml = leaf_model.SplitLogMarginalLikelihood(left_suff_stat, right_suff_stat, global_variance);
477 double no_split_log_ml = leaf_model.NoSplitLogMarginalLikelihood(node_suff_stat, global_variance);
478
479 return std::tuple<double, double, data_size_t, data_size_t>(split_log_ml, no_split_log_ml, left_n, right_n);
480}
481
482template <typename LeafModel>
483static inline void AdjustStateBeforeTreeSampling(ForestTracker& tracker, LeafModel& leaf_model, ForestDataset& dataset,
484 ColumnVector& residual, TreePrior& tree_prior, bool backfitting, Tree* tree, int tree_num) {
485 if constexpr (std::is_same_v<LeafModel, CloglogOrdinalLeafModel>) {
486 UpdateCLogLogModelTree(tracker, dataset, residual, tree, tree_num, leaf_model.RequiresBasis(), false);
487 } else if (backfitting) {
488 UpdateMeanModelTree(tracker, dataset, residual, tree, tree_num, leaf_model.RequiresBasis(), std::plus<double>(), false);
489 } else {
490 // TODO: think about a generic way to store "state" corresponding to the other models?
491 UpdateVarModelTree(tracker, dataset, residual, tree, tree_num, leaf_model.RequiresBasis(), std::minus<double>(), false);
492 }
493}
494
495template <typename LeafModel>
496static inline void AdjustStateAfterTreeSampling(ForestTracker& tracker, LeafModel& leaf_model, ForestDataset& dataset,
497 ColumnVector& residual, TreePrior& tree_prior, bool backfitting, Tree* tree, int tree_num) {
498 if constexpr (std::is_same_v<LeafModel, CloglogOrdinalLeafModel>) {
499 UpdateCLogLogModelTree(tracker, dataset, residual, tree, tree_num, leaf_model.RequiresBasis(), true);
500 } else if (backfitting) {
501 UpdateMeanModelTree(tracker, dataset, residual, tree, tree_num, leaf_model.RequiresBasis(), std::minus<double>(), true);
502 } else {
503 // TODO: think about a generic way to store "state" corresponding to the other models?
504 UpdateVarModelTree(tracker, dataset, residual, tree, tree_num, leaf_model.RequiresBasis(), std::plus<double>(), true);
505 }
506}
507
508template <typename LeafModel, typename LeafSuffStat, typename... LeafSuffStatConstructorArgs>
509static inline void SampleSplitRule(Tree* tree, ForestTracker& tracker, LeafModel& leaf_model, ForestDataset& dataset, ColumnVector& residual,
510 TreePrior& tree_prior, std::mt19937& gen, int tree_num, double global_variance, int cutpoint_grid_size,
511 std::unordered_map<int, std::pair<data_size_t, data_size_t>>& node_index_map, std::deque<node_t>& split_queue,
513 std::vector<FeatureType>& feature_types, std::vector<bool> feature_subset, int num_threads, LeafSuffStatConstructorArgs&... leaf_suff_stat_args) {
514 // Leaf depth
515 int leaf_depth = tree->GetDepth(node_id);
516
517 // Maximum leaf depth
518 int32_t max_depth = tree_prior.GetMaxDepth();
519
520 if ((max_depth == -1) || (leaf_depth < max_depth)) {
521 // Vector of vectors to store results for each feature
522 int p = dataset.NumCovariates();
523 std::vector<std::vector<double>> feature_log_cutpoint_evaluations(p + 1);
524 std::vector<std::vector<double>> feature_cutpoint_values(p + 1);
525 std::vector<int> feature_cutpoint_counts(p + 1, 0);
527
528 // Evaluate all possible cutpoints according to the leaf node model,
529 // recording their log-likelihood and other split information in a series of vectors.
530
531 // Initialize node sufficient statistics
533
534 // Accumulate aggregate sufficient statistic for the node to be split
536
537 // Compute the "no split" log marginal likelihood
538 double no_split_log_ml = leaf_model.NoSplitLogMarginalLikelihood(node_suff_stat, global_variance);
539
540 // Unpack data
541 Eigen::MatrixXd& covariates = dataset.GetCovariates();
542 Eigen::VectorXd& outcome = residual.GetData();
543 Eigen::VectorXd var_weights;
544 bool has_weights = dataset.HasVarWeights();
545 if (has_weights) var_weights = dataset.GetVarWeights();
546
547 // Minimum size of newly created leaf nodes (used to rule out invalid splits)
548 int32_t min_samples_in_leaf = tree_prior.GetMinSamplesLeaf();
549
550 // Compute sufficient statistics for each possible split
552 if (num_threads == -1) {
553 num_threads = GetOptimalThreadCount(static_cast<int>(covariates.cols() * covariates.rows()));
554 }
555
556 // Initialize cutpoint grid container
557 CutpointGridContainer cutpoint_grid_container(covariates, outcome, cutpoint_grid_size);
558
559 // Evaluate all possible splits for each feature in parallel
560 StochTree::ParallelFor(0, covariates.cols(), num_threads, [&](int j) {
561 if ((std::abs(variable_weights.at(j)) > kEpsilon) && (feature_subset[j])) {
562 // Enumerate cutpoint strides
563 cutpoint_grid_container.CalculateStrides(covariates, outcome, tracker.GetSortedNodeSampleTracker(), node_id, node_begin, node_end, j, feature_types);
564
565 // Left and right node sufficient statistics
566 LeafSuffStat left_suff_stat = LeafSuffStat(leaf_suff_stat_args...);
567 LeafSuffStat right_suff_stat = LeafSuffStat(leaf_suff_stat_args...);
568
569 // Iterate through possible cutpoints
570 int32_t num_feature_cutpoints = cutpoint_grid_container.NumCutpoints(j);
571 FeatureType feature_type = feature_types[j];
572 // Since we partition an entire cutpoint bin to the left, we must stop one bin before the total number of cutpoint bins
573 for (data_size_t cutpoint_idx = 0; cutpoint_idx < (num_feature_cutpoints - 1); cutpoint_idx++) {
574 data_size_t current_bin_begin = cutpoint_grid_container.BinStartIndex(cutpoint_idx, j);
575 data_size_t current_bin_size = cutpoint_grid_container.BinLength(cutpoint_idx, j);
576 data_size_t next_bin_begin = cutpoint_grid_container.BinStartIndex(cutpoint_idx + 1, j);
577
578 // Accumulate sufficient statistics for the left node
579 AccumulateCutpointBinSuffStat<LeafSuffStat>(left_suff_stat, tracker, cutpoint_grid_container, dataset, residual,
580 global_variance, tree_num, node_id, j, cutpoint_idx);
581
582 // Compute the corresponding right node sufficient statistics
583 right_suff_stat.SubtractSuffStat(node_suff_stat, left_suff_stat);
584
585 // Store the bin index as the "cutpoint value" - we can use this to query the actual split
586 // value or the set of split categories later on once a split is chose
587 double cutoff_value = cutpoint_idx;
588
589 // Only include cutpoint for consideration if it defines a valid split in the training data
590 bool valid_split = (left_suff_stat.SampleGreaterThanEqual(min_samples_in_leaf) &&
591 right_suff_stat.SampleGreaterThanEqual(min_samples_in_leaf));
592 if (valid_split) {
593 feature_cutpoint_counts[j]++;
594 // Add to split rule vector
595 feature_cutpoint_values[j].push_back(cutoff_value);
596 // Add the log marginal likelihood of the split to the split eval vector
597 double split_log_ml = leaf_model.SplitLogMarginalLikelihood(left_suff_stat, right_suff_stat, global_variance);
598 feature_log_cutpoint_evaluations[j].push_back(split_log_ml);
599 }
600 }
601 }
602 });
603
604 // Compute total number of cutpoints
605 valid_cutpoint_count = std::accumulate(feature_cutpoint_counts.begin(), feature_cutpoint_counts.end(), 0);
606
607 // Add the log marginal likelihood of the "no-split" option (adjusted for tree prior and cutpoint size per the XBART paper)
609
610 // Compute an adjustment to reflect the no split prior probability and the number of cutpoints
612 double alpha = tree_prior.GetAlpha();
613 double beta = tree_prior.GetBeta();
614 int node_depth = tree->GetDepth(node_id);
615 if (valid_cutpoint_count == 0) {
616 bart_prior_no_split_adj = std::log(((std::pow(1 + node_depth, beta)) / alpha) - 1.0);
617 } else {
618 bart_prior_no_split_adj = std::log(((std::pow(1 + node_depth, beta)) / alpha) - 1.0) + std::log(valid_cutpoint_count);
619 }
621
622 // Convert log marginal likelihood to marginal likelihood, normalizing by the maximum log-likelihood
623 double largest_ml = -std::numeric_limits<double>::infinity();
624 for (int j = 0; j < p + 1; j++) {
627 ;
629 }
630 }
631 std::vector<std::vector<double>> feature_cutpoint_evaluations(p + 1);
632 for (int j = 0; j < p + 1; j++) {
635 for (int i = 0; i < feature_log_cutpoint_evaluations[j].size(); i++) {
637 }
638 }
639 }
640
641 // Compute sum of marginal likelihoods for each feature
642 std::vector<double> feature_total_cutpoint_evaluations(p + 1, 0.0);
643 for (int j = 0; j < p + 1; j++) {
646 } else {
648 }
649 }
650
651 // First, sample a feature according to feature_total_cutpoint_evaluations
652 int feature_chosen = sample_discrete_stateless(gen, feature_total_cutpoint_evaluations);
653
654 // Then, sample a cutpoint according to feature_cutpoint_evaluations[feature_chosen]
656
657 if (feature_chosen == p) {
658 // "No split" sampled, don't split or add any nodes to split queue
659 return;
660 } else {
661 // Split sampled
663 FeatureType feature_type = feature_types[feature_split];
665 // Perform all of the relevant "split" operations in the model, tree and training dataset
666
667 // Compute node sample size
669
670 // Actual numeric cutpoint used for ordered categorical and numeric features
671 double split_value_numeric;
672 TreeSplit tree_split;
673
674 // We will use these later in the model expansion
678 double feature_value;
679 bool split_true;
680
681 if (feature_type == FeatureType::kUnorderedCategorical) {
682 // Determine the number of categories available in a categorical split and the set of categories that route observations to the left node after split
683 int num_categories;
684 std::vector<std::uint32_t> categories = cutpoint_grid_container.CutpointVector(static_cast<std::uint32_t>(split_value), feature_split);
685 tree_split = TreeSplit(categories);
686 } else if (feature_type == FeatureType::kOrderedCategorical) {
687 // Convert the bin split to an actual split value
688 split_value_numeric = cutpoint_grid_container.CutpointValue(static_cast<std::uint32_t>(split_value), feature_split);
689 tree_split = TreeSplit(split_value_numeric);
690 } else if (feature_type == FeatureType::kNumeric) {
691 // Convert the bin split to an actual split value
692 split_value_numeric = cutpoint_grid_container.CutpointValue(static_cast<std::uint32_t>(split_value), feature_split);
693 tree_split = TreeSplit(split_value_numeric);
694 } else {
695 Log::Fatal("Invalid split type");
696 }
697
698 // Add split to tree and trackers
699 AddSplitToModel(tracker, dataset, tree_prior, tree_split, gen, tree, tree_num, node_id, feature_split, true, num_threads);
700
701 // Determine the number of observation in the newly created left node
702 int left_node = tree->LeftChild(node_id);
703 int right_node = tree->RightChild(node_id);
704 auto left_begin_iter = tracker.SortedNodeBeginIterator(left_node, feature_split);
705 auto left_end_iter = tracker.SortedNodeEndIterator(left_node, feature_split);
706 for (auto i = left_begin_iter; i < left_end_iter; i++) {
707 left_n += 1;
708 }
709
710 // Add the begin and end indices for the new left and right nodes to node_index_map
711 node_index_map.insert({left_node, std::make_pair(node_begin, node_begin + left_n)});
712 node_index_map.insert({right_node, std::make_pair(node_begin + left_n, node_end)});
713
714 // Add the left and right nodes to the split tracker
715 split_queue.push_front(right_node);
716 split_queue.push_front(left_node);
717 }
718 }
719}
720
721template <typename LeafModel, typename LeafSuffStat, typename... LeafSuffStatConstructorArgs>
722static inline void GFRSampleTreeOneIter(Tree* tree, ForestTracker& tracker, ForestContainer& forests, LeafModel& leaf_model, ForestDataset& dataset,
723 ColumnVector& residual, TreePrior& tree_prior, std::mt19937& gen, std::vector<double>& variable_weights,
724 int tree_num, double global_variance, std::vector<FeatureType>& feature_types, int cutpoint_grid_size,
725 int num_features_subsample, int num_threads, LeafSuffStatConstructorArgs&... leaf_suff_stat_args) {
726 int root_id = Tree::kRoot;
727 int curr_node_id;
728 data_size_t curr_node_begin;
729 data_size_t curr_node_end;
730 data_size_t n = dataset.GetCovariates().rows();
731 int p = dataset.GetCovariates().cols();
732
733 // Subsample features (if requested)
734 std::vector<bool> feature_subset(p, true);
735 if (num_features_subsample < p) {
736 // Check if the number of (meaningfully) nonzero selection probabilities is greater than num_features_subsample
737 int number_nonzero_weights = 0;
738 for (int j = 0; j < p; j++) {
739 if (std::abs(variable_weights.at(j)) > kEpsilon) {
740 number_nonzero_weights++;
741 }
742 }
743 if (number_nonzero_weights > num_features_subsample) {
744 // Sample with replacement according to variable_weights
745 std::vector<int> feature_indices(p);
746 std::iota(feature_indices.begin(), feature_indices.end(), 0);
747 std::vector<int> features_selected(num_features_subsample);
748 sample_without_replacement<int, double>(
749 features_selected.data(), variable_weights.data(), feature_indices.data(),
750 p, num_features_subsample, gen);
751 for (int i = 0; i < p; i++) {
752 feature_subset.at(i) = false;
753 }
754 for (const auto& feat : features_selected) {
755 feature_subset.at(feat) = true;
756 }
757 }
758 }
759
760 // Mapping from node id to start and end points of sorted indices
761 std::unordered_map<int, std::pair<data_size_t, data_size_t>> node_index_map;
762 node_index_map.insert({root_id, std::make_pair(0, n)});
763 std::pair<data_size_t, data_size_t> begin_end;
764 // Add root node to the split queue
765 std::deque<node_t> split_queue;
766 split_queue.push_back(Tree::kRoot);
767 // Run the "GrowFromRoot" procedure using a stack in place of recursion
768 while (!split_queue.empty()) {
769 // Remove the next node from the queue
770 curr_node_id = split_queue.front();
771 split_queue.pop_front();
772 // Determine the beginning and ending indices of the left and right nodes
773 begin_end = node_index_map[curr_node_id];
774 curr_node_begin = begin_end.first;
775 curr_node_end = begin_end.second;
776 // Draw a split rule at random
777 SampleSplitRule<LeafModel, LeafSuffStat, LeafSuffStatConstructorArgs...>(
778 tree, tracker, leaf_model, dataset, residual, tree_prior, gen, tree_num, global_variance, cutpoint_grid_size,
779 node_index_map, split_queue, curr_node_id, curr_node_begin, curr_node_end, variable_weights, feature_types,
780 feature_subset, num_threads, leaf_suff_stat_args...);
781 }
782}
783
815template <typename LeafModel, typename LeafSuffStat, typename... LeafSuffStatConstructorArgs>
816static inline void GFRSampleOneIter(TreeEnsemble& active_forest, ForestTracker& tracker, ForestContainer& forests, LeafModel& leaf_model, ForestDataset& dataset,
817 ColumnVector& residual, TreePrior& tree_prior, std::mt19937& gen, std::vector<double>& variable_weights,
818 std::vector<int>& sweep_update_indices, double global_variance, std::vector<FeatureType>& feature_types, int cutpoint_grid_size,
819 bool keep_forest, bool pre_initialized, bool backfitting, int num_features_subsample, int num_threads, LeafSuffStatConstructorArgs&... leaf_suff_stat_args) {
820 // Run the GFR algorithm for each tree
821 int num_trees = forests.NumTrees();
822 for (const int& i : sweep_update_indices) {
823 // Adjust any model state needed to run a tree sampler
824 // For models that involve Bayesian backfitting, this amounts to adding tree i's
825 // predictions back to the residual (thus, training a model on the "partial residual")
826 // For more general "blocked MCMC" models, this might require changes to a ForestTracker or Dataset object
827 Tree* tree = active_forest.GetTree(i);
828 AdjustStateBeforeTreeSampling<LeafModel>(tracker, leaf_model, dataset, residual, tree_prior, backfitting, tree, i);
829
830 // Reset the tree and sample trackers
831 active_forest.ResetInitTree(i);
832 tracker.ResetRoot(dataset.GetCovariates(), feature_types, i);
833 tree = active_forest.GetTree(i);
834
835 // Sample tree i
836 GFRSampleTreeOneIter<LeafModel, LeafSuffStat, LeafSuffStatConstructorArgs...>(
837 tree, tracker, forests, leaf_model, dataset, residual, tree_prior, gen,
838 variable_weights, i, global_variance, feature_types, cutpoint_grid_size,
839 num_features_subsample, num_threads, leaf_suff_stat_args...);
840
841 // Sample leaf parameters for tree i
842 tree = active_forest.GetTree(i);
843 leaf_model.SampleLeafParameters(dataset, tracker, residual, tree, i, global_variance, gen);
844
845 // Adjust any model state needed to run a tree sampler
846 // For models that involve Bayesian backfitting, this amounts to subtracting tree i's
847 // predictions back out of the residual (thus, using an updated "partial residual" in the following interation).
848 // For more general "blocked MCMC" models, this might require changes to a ForestTracker or Dataset object
849 AdjustStateAfterTreeSampling<LeafModel>(tracker, leaf_model, dataset, residual, tree_prior, backfitting, tree, i);
850 }
851
852 if (keep_forest) {
853 forests.AddSample(active_forest);
854 }
855}
856
857template <typename LeafModel, typename LeafSuffStat, typename... LeafSuffStatConstructorArgs>
858static inline void MCMCGrowTreeOneIter(Tree* tree, ForestTracker& tracker, LeafModel& leaf_model, ForestDataset& dataset, ColumnVector& residual,
859 TreePrior& tree_prior, std::mt19937& gen, int tree_num, std::vector<double>& variable_weights,
860 double global_variance, double prob_grow_old, int num_threads, LeafSuffStatConstructorArgs&... leaf_suff_stat_args) {
861 // Extract dataset information
862 data_size_t n = dataset.GetCovariates().rows();
863
864 // Choose a leaf node at random
865 int num_leaves = tree->NumLeaves();
866 std::vector<int> leaves = tree->GetLeaves();
867 std::vector<double> leaf_weights(num_leaves);
868 std::fill(leaf_weights.begin(), leaf_weights.end(), 1.0 / num_leaves);
869 walker_vose leaf_dist(leaf_weights.begin(), leaf_weights.end());
870 int leaf_chosen = leaves[leaf_dist(gen)];
871 int leaf_depth = tree->GetDepth(leaf_chosen);
872
873 // Maximum leaf depth
874 int32_t max_depth = tree_prior.GetMaxDepth();
875
876 // Terminate early if cannot be split
877 bool accept;
878 if ((leaf_depth >= max_depth) && (max_depth != -1)) {
879 accept = false;
880 } else {
881 // Select a split variable at random
882 int p = dataset.GetCovariates().cols();
883 CHECK_EQ(variable_weights.size(), p);
884 walker_vose var_dist(variable_weights.begin(), variable_weights.end());
885 int var_chosen = var_dist(gen);
886
887 // Determine the range of possible cutpoints
888 // TODO: specialize this for binary / ordered categorical / unordered categorical variables
889 double var_min, var_max;
890 VarSplitRange(tracker, dataset, tree_num, leaf_chosen, var_chosen, var_min, var_max);
891 if (var_max <= var_min) {
892 return;
893 }
894
895 // Split based on var_min to var_max in a given node
896 double split_point_chosen = standard_uniform_draw_53bit(gen) * (var_max - var_min) + var_min;
897
898 // Create a split object
899 TreeSplit split = TreeSplit(split_point_chosen);
900
901 // Compute the marginal likelihood of split and no split, given the leaf prior
902 std::tuple<double, double, int32_t, int32_t> split_eval = EvaluateProposedSplit<LeafModel, LeafSuffStat, LeafSuffStatConstructorArgs...>(
903 dataset, tracker, residual, leaf_model, split, tree_num, leaf_chosen, var_chosen, global_variance, num_threads, leaf_suff_stat_args...);
904 double split_log_marginal_likelihood = std::get<0>(split_eval);
905 double no_split_log_marginal_likelihood = std::get<1>(split_eval);
906 int32_t left_n = std::get<2>(split_eval);
907 int32_t right_n = std::get<3>(split_eval);
908
909 // Reject the split if either of the left and right nodes are smaller than tree_prior.GetMinSamplesLeaf()
910 bool left_node_sample_cutoff = left_n >= tree_prior.GetMinSamplesLeaf();
911 bool right_node_sample_cutoff = right_n >= tree_prior.GetMinSamplesLeaf();
912 if ((left_node_sample_cutoff) && (right_node_sample_cutoff)) {
913 // Determine probability of growing the split node and its two new left and right nodes
914 double pg = tree_prior.GetAlpha() * std::pow(1 + leaf_depth, -tree_prior.GetBeta());
915 double pgl = tree_prior.GetAlpha() * std::pow(1 + leaf_depth + 1, -tree_prior.GetBeta());
916 double pgr = tree_prior.GetAlpha() * std::pow(1 + leaf_depth + 1, -tree_prior.GetBeta());
917
918 // Determine whether a "grow" move is possible from the newly formed tree
919 // in order to compute the probability of choosing "prune" from the new tree
920 // (which is always possible by construction)
921 bool non_constant = NodesNonConstantAfterSplit(dataset, tracker, split, tree_num, leaf_chosen, var_chosen);
922 bool min_samples_left_check = left_n >= 2 * tree_prior.GetMinSamplesLeaf();
923 bool min_samples_right_check = right_n >= 2 * tree_prior.GetMinSamplesLeaf();
924 double prob_prune_new;
925 if (non_constant && (min_samples_left_check || min_samples_right_check)) {
926 prob_prune_new = 0.5;
927 } else {
928 prob_prune_new = 1.0;
929 }
930
931 // Determine the number of leaves in the current tree and leaf parents in the proposed tree
932 int num_leaf_parents = tree->NumLeafParents();
933 double p_leaf = 1 / static_cast<double>(num_leaves);
934 double p_leaf_parent = 1 / static_cast<double>(num_leaf_parents + 1);
935
936 // Compute the final MH ratio
937 double log_mh_ratio = (std::log(pg) + std::log(1 - pgl) + std::log(1 - pgr) - std::log(1 - pg) + std::log(prob_prune_new) +
938 std::log(p_leaf_parent) - std::log(prob_grow_old) - std::log(p_leaf) - no_split_log_marginal_likelihood + split_log_marginal_likelihood);
939 // Threshold at 0
940 if (log_mh_ratio > 0) {
941 log_mh_ratio = 0;
942 }
943
944 // Draw a uniform random variable and accept/reject the proposal on this basis
945 double log_acceptance_prob = std::log(standard_uniform_draw_53bit(gen));
946 if (log_acceptance_prob <= log_mh_ratio) {
947 accept = true;
948 AddSplitToModel(tracker, dataset, tree_prior, split, gen, tree, tree_num, leaf_chosen, var_chosen, false, num_threads);
949 } else {
950 accept = false;
951 }
952
953 } else {
954 accept = false;
955 }
956 }
957}
958
959template <typename LeafModel, typename LeafSuffStat, typename... LeafSuffStatConstructorArgs>
960static inline void MCMCPruneTreeOneIter(Tree* tree, ForestTracker& tracker, LeafModel& leaf_model, ForestDataset& dataset, ColumnVector& residual,
961 TreePrior& tree_prior, std::mt19937& gen, int tree_num, double global_variance, int num_threads,
962 LeafSuffStatConstructorArgs&... leaf_suff_stat_args) {
963 // Choose a "leaf parent" node at random
964 int num_leaves = tree->NumLeaves();
965 int num_leaf_parents = tree->NumLeafParents();
966 std::vector<int> leaf_parents = tree->GetLeafParents();
967 std::vector<double> leaf_parent_weights(num_leaf_parents);
968 std::fill(leaf_parent_weights.begin(), leaf_parent_weights.end(), 1.0 / num_leaf_parents);
969 walker_vose leaf_parent_dist(leaf_parent_weights.begin(), leaf_parent_weights.end());
970 int leaf_parent_chosen = leaf_parents[leaf_parent_dist(gen)];
971 int leaf_parent_depth = tree->GetDepth(leaf_parent_chosen);
972 int left_node = tree->LeftChild(leaf_parent_chosen);
973 int right_node = tree->RightChild(leaf_parent_chosen);
974 int feature_split = tree->SplitIndex(leaf_parent_chosen);
975
976 // Compute the marginal likelihood for the leaf parent and its left and right nodes
977 std::tuple<double, double, int32_t, int32_t> split_eval = EvaluateExistingSplit<LeafModel, LeafSuffStat, LeafSuffStatConstructorArgs...>(
978 dataset, tracker, residual, leaf_model, global_variance, tree_num, leaf_parent_chosen, left_node, right_node, leaf_suff_stat_args...);
979 double split_log_marginal_likelihood = std::get<0>(split_eval);
980 double no_split_log_marginal_likelihood = std::get<1>(split_eval);
981 int32_t left_n = std::get<2>(split_eval);
982 int32_t right_n = std::get<3>(split_eval);
983
984 // Determine probability of growing the split node and its two new left and right nodes
985 double pg = tree_prior.GetAlpha() * std::pow(1 + leaf_parent_depth, -tree_prior.GetBeta());
986 double pgl = tree_prior.GetAlpha() * std::pow(1 + leaf_parent_depth + 1, -tree_prior.GetBeta());
987 double pgr = tree_prior.GetAlpha() * std::pow(1 + leaf_parent_depth + 1, -tree_prior.GetBeta());
988
989 // Determine whether a "prune" move is possible from the new tree,
990 // in order to compute the probability of choosing "grow" from the new tree
991 // (which is always possible by construction)
992 bool non_root_tree = tree->NumNodes() > 1;
993 double prob_grow_new;
994 if (non_root_tree) {
995 prob_grow_new = 0.5;
996 } else {
997 prob_grow_new = 1.0;
998 }
999
1000 // Determine whether a "grow" move was possible from the old tree,
1001 // in order to compute the probability of choosing "prune" from the old tree
1002 bool non_constant_left = NodeNonConstant(dataset, tracker, tree_num, left_node);
1003 bool non_constant_right = NodeNonConstant(dataset, tracker, tree_num, right_node);
1004 double prob_prune_old;
1005 if (non_constant_left && non_constant_right) {
1006 prob_prune_old = 0.5;
1007 } else {
1008 prob_prune_old = 1.0;
1009 }
1010
1011 // Determine the number of leaves in the current tree and leaf parents in the proposed tree
1012 double p_leaf = 1 / static_cast<double>(num_leaves - 1);
1013 double p_leaf_parent = 1 / static_cast<double>(num_leaf_parents);
1014
1015 // Compute the final MH ratio
1016 double log_mh_ratio = (std::log(1 - pg) - std::log(pg) - std::log(1 - pgl) - std::log(1 - pgr) + std::log(prob_prune_old) +
1017 std::log(p_leaf) - std::log(prob_grow_new) - std::log(p_leaf_parent) + no_split_log_marginal_likelihood - split_log_marginal_likelihood);
1018 // Threshold at 0
1019 if (log_mh_ratio > 0) {
1020 log_mh_ratio = 0;
1021 }
1022
1023 // Draw a uniform random variable and accept/reject the proposal on this basis
1024 bool accept;
1025 double log_acceptance_prob = std::log(standard_uniform_draw_53bit(gen));
1026 if (log_acceptance_prob <= log_mh_ratio) {
1027 accept = true;
1028 RemoveSplitFromModel(tracker, dataset, tree_prior, gen, tree, tree_num, leaf_parent_chosen, left_node, right_node, false);
1029 } else {
1030 accept = false;
1031 }
1032}
1033
1034template <typename LeafModel, typename LeafSuffStat, typename... LeafSuffStatConstructorArgs>
1035static inline void MCMCSampleTreeOneIter(Tree* tree, ForestTracker& tracker, ForestContainer& forests, LeafModel& leaf_model, ForestDataset& dataset,
1036 ColumnVector& residual, TreePrior& tree_prior, std::mt19937& gen, std::vector<double>& variable_weights,
1037 int tree_num, double global_variance, int num_threads, LeafSuffStatConstructorArgs&... leaf_suff_stat_args) {
1038 // Determine whether it is possible to grow any of the leaves
1039 bool grow_possible = false;
1040 std::vector<int> leaves = tree->GetLeaves();
1041 for (auto& leaf : leaves) {
1042 if (tracker.UnsortedNodeSize(tree_num, leaf) > 2 * tree_prior.GetMinSamplesLeaf()) {
1043 grow_possible = true;
1044 break;
1045 }
1046 }
1047
1048 // Determine whether it is possible to prune the tree
1049 bool prune_possible = false;
1050 if (tree->NumValidNodes() > 1) {
1051 prune_possible = true;
1052 }
1053
1054 // Determine the relative probability of grow vs prune (0 = grow, 1 = prune)
1055 double prob_grow;
1056 std::vector<double> step_probs(2);
1057 if (grow_possible && prune_possible) {
1058 step_probs = {0.5, 0.5};
1059 prob_grow = 0.5;
1060 } else if (!grow_possible && prune_possible) {
1061 step_probs = {0.0, 1.0};
1062 prob_grow = 0.0;
1063 } else if (grow_possible && !prune_possible) {
1064 step_probs = {1.0, 0.0};
1065 prob_grow = 1.0;
1066 } else {
1067 Log::Fatal("In this tree, neither grow nor prune is possible");
1068 }
1069 walker_vose step_dist(step_probs.begin(), step_probs.end());
1070
1071 // Draw a split rule at random
1072 data_size_t step_chosen = step_dist(gen);
1073 bool accept;
1074
1075 if (step_chosen == 0) {
1076 MCMCGrowTreeOneIter<LeafModel, LeafSuffStat, LeafSuffStatConstructorArgs...>(
1077 tree, tracker, leaf_model, dataset, residual, tree_prior, gen, tree_num, variable_weights, global_variance, prob_grow, num_threads, leaf_suff_stat_args...);
1078 } else {
1079 MCMCPruneTreeOneIter<LeafModel, LeafSuffStat, LeafSuffStatConstructorArgs...>(
1080 tree, tracker, leaf_model, dataset, residual, tree_prior, gen, tree_num, global_variance, num_threads, leaf_suff_stat_args...);
1081 }
1082}
1083
1112template <typename LeafModel, typename LeafSuffStat, typename... LeafSuffStatConstructorArgs>
1113static inline void MCMCSampleOneIter(TreeEnsemble& active_forest, ForestTracker& tracker, ForestContainer& forests, LeafModel& leaf_model, ForestDataset& dataset,
1114 ColumnVector& residual, TreePrior& tree_prior, std::mt19937& gen, std::vector<double>& variable_weights,
1115 std::vector<int>& sweep_update_indices, double global_variance, bool keep_forest, bool pre_initialized, bool backfitting, int num_threads,
1116 LeafSuffStatConstructorArgs&... leaf_suff_stat_args) {
1117 // Run the MCMC algorithm for each tree
1118 int num_trees = forests.NumTrees();
1119 for (const int& i : sweep_update_indices) {
1120 // Adjust any model state needed to run a tree sampler
1121 // For models that involve Bayesian backfitting, this amounts to adding tree i's
1122 // predictions back to the residual (thus, training a model on the "partial residual")
1123 // For more general "blocked MCMC" models, this might require changes to a ForestTracker or Dataset object
1124 Tree* tree = active_forest.GetTree(i);
1125 AdjustStateBeforeTreeSampling<LeafModel>(tracker, leaf_model, dataset, residual, tree_prior, backfitting, tree, i);
1126
1127 // Sample tree i
1128 tree = active_forest.GetTree(i);
1129 MCMCSampleTreeOneIter<LeafModel, LeafSuffStat, LeafSuffStatConstructorArgs...>(
1130 tree, tracker, forests, leaf_model, dataset, residual, tree_prior, gen, variable_weights, i,
1131 global_variance, num_threads, leaf_suff_stat_args...);
1132
1133 // Sample leaf parameters for tree i
1134 tree = active_forest.GetTree(i);
1135 leaf_model.SampleLeafParameters(dataset, tracker, residual, tree, i, global_variance, gen);
1136
1137 // Adjust any model state needed to run a tree sampler
1138 // For models that involve Bayesian backfitting, this amounts to subtracting tree i's
1139 // predictions back out of the residual (thus, using an updated "partial residual" in the following interation).
1140 // For more general "blocked MCMC" models, this might require changes to a ForestTracker or Dataset object
1141 AdjustStateAfterTreeSampling<LeafModel>(tracker, leaf_model, dataset, residual, tree_prior, backfitting, tree, i);
1142 }
1143
1144 if (keep_forest) {
1145 forests.AddSample(active_forest);
1146 }
1147}
1148
// end of sampling_group
1150
1151} // namespace StochTree
1152
1153#endif // STOCHTREE_TREE_SAMPLER_H_
Internal wrapper around Eigen::VectorXd interface for univariate floating point data....
Definition data.h:193
Container of TreeEnsemble forest objects. This is the primary (in-memory) storage interface for multi...
Definition container.h:24
void AddSample(TreeEnsemble &forest)
Add a new forest to the container by copying forest.
API for loading and accessing data used to sample tree ensembles The covariates / bases / weights use...
Definition data.h:272
Eigen::MatrixXd & GetCovariates()
Return a reference to the raw Eigen::MatrixXd storing the covariate data.
Definition data.h:384
"Superclass" wrapper around tracking data structures for forest sampling algorithms
Definition partition_tracker.h:46
Class storing a "forest," or an ensemble of decision trees.
Definition ensemble.h:31
Tree * GetTree(int i)
Return a pointer to a tree in the forest.
Definition ensemble.h:134
void ResetInitTree(int i)
Reset a single tree in an ensemble.
Definition ensemble.h:163
Definition prior.h:44
Representation of arbitrary tree split rules, including numeric split rules (X[,i] <= c) and categori...
Definition tree.h:956
Decision tree data structure.
Definition tree.h:66
static void MCMCSampleOneIter(TreeEnsemble &active_forest, ForestTracker &tracker, ForestContainer &forests, LeafModel &leaf_model, ForestDataset &dataset, ColumnVector &residual, TreePrior &tree_prior, std::mt19937 &gen, std::vector< double > &variable_weights, std::vector< int > &sweep_update_indices, double global_variance, bool keep_forest, bool pre_initialized, bool backfitting, int num_threads, LeafSuffStatConstructorArgs &... leaf_suff_stat_args)
Runs one iteration of the MCMC sampler for a tree ensemble model, which consists of two steps for eve...
Definition tree_sampler.h:1113
static void VarSplitRange(ForestTracker &tracker, ForestDataset &dataset, int tree_num, int leaf_split, int feature_split, double &var_min, double &var_max)
Computer the range of available split values for a continuous variable, given the current structure o...
Definition tree_sampler.h:48
static bool NodesNonConstantAfterSplit(ForestDataset &dataset, ForestTracker &tracker, TreeSplit &split, int tree_num, int leaf_split, int feature_split)
Determines whether a proposed split creates two leaf nodes with constant values for every feature (th...
Definition tree_sampler.h:80
static void GFRSampleOneIter(TreeEnsemble &active_forest, ForestTracker &tracker, ForestContainer &forests, LeafModel &leaf_model, ForestDataset &dataset, ColumnVector &residual, TreePrior &tree_prior, std::mt19937 &gen, std::vector< double > &variable_weights, std::vector< int > &sweep_update_indices, double global_variance, std::vector< FeatureType > &feature_types, int cutpoint_grid_size, bool keep_forest, bool pre_initialized, bool backfitting, int num_features_subsample, int num_threads, LeafSuffStatConstructorArgs &... leaf_suff_stat_args)
Definition tree_sampler.h:816
A collection of random number generation utilities.
Definition bart.h:15
double standard_uniform_draw_53bit(std::mt19937 &gen)
Definition distributions.h:37