From 01104aadc98052fb880cd6a8bb2315829b01a942 Mon Sep 17 00:00:00 2001 From: Bilyana Indzheva Date: Fri, 31 May 2024 17:20:09 +0300 Subject: [PATCH 01/10] implement solution --- onnxruntime/core/providers/cpu/ml/ml_common.h | 6 +- .../providers/cpu/ml/tree_ensemble_common.h | 148 +++++++++++++++--- .../providers/cpu/ml/treeregressor_test.cc | 84 ++++++++++ 3 files changed, 215 insertions(+), 23 deletions(-) diff --git a/onnxruntime/core/providers/cpu/ml/ml_common.h b/onnxruntime/core/providers/cpu/ml/ml_common.h index ed108eade05ab..82a38691b58e5 100644 --- a/onnxruntime/core/providers/cpu/ml/ml_common.h +++ b/onnxruntime/core/providers/cpu/ml/ml_common.h @@ -27,7 +27,8 @@ enum NODE_MODE : uint8_t { BRANCH_GTE = 6, BRANCH_GT = 8, BRANCH_EQ = 10, - BRANCH_NEQ = 12 + BRANCH_NEQ = 12, + BRANCH_SM = 14 }; static inline NODE_MODE MakeTreeNodeMode(const std::string& input) { @@ -49,6 +50,9 @@ static inline NODE_MODE MakeTreeNodeMode(const std::string& input) { if (input == "BRANCH_EQ") { return NODE_MODE::BRANCH_EQ; } + if (input == "BRANCH_SM") { + return NODE_MODE::BRANCH_SM; + } return NODE_MODE::BRANCH_NEQ; } diff --git a/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h b/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h index 8f847fe66aa73..aba22afdf5694 100644 --- a/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h +++ b/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h @@ -8,6 +8,8 @@ #include "core/platform/threadpool.h" #include "tree_ensemble_helper.h" +#include + namespace onnxruntime { namespace ml { namespace detail { @@ -87,11 +89,17 @@ class TreeEnsembleCommon : public TreeEnsembleCommonAttributes { void ComputeAgg(concurrency::ThreadPool* ttp, const Tensor* X, Tensor* Y, Tensor* label, const AGG& agg) const; private: + bool CheckIfSubtreesAreEqual(const size_t left_id, const size_t right_id, const int64_t tree_id, const InlinedVector& cmodes, + const InlinedVector& truenode_ids, const InlinedVector& falsenode_ids, const std::vector& nodes_featureids, + const std::vector& nodes_values_as_tensor, const std::vector& node_values, + const std::vector& target_class_weights, const std::vector& target_class_weights_as_tensor, + const InlinedVector& node_tree_ids, InlinedVector> indices); size_t AddNodes(const size_t i, const InlinedVector& cmodes, const InlinedVector& truenode_ids, const InlinedVector& falsenode_ids, const std::vector& nodes_featureids, const std::vector& nodes_values_as_tensor, const std::vector& node_values, const std::vector& nodes_missing_value_tracks_true, std::vector& updated_mapping, - int64_t tree_id, const InlinedVector& node_tree_ids); + int64_t tree_id, const InlinedVector& node_tree_ids, const std::vector& target_class_weights, + const std::vector& target_class_weights_as_tensor, InlinedVector> indices); }; template @@ -270,6 +278,16 @@ Status TreeEnsembleCommon::Init( } } + // Sort targets + InlinedVector> indices; + indices.reserve(target_class_nodeids.size()); + for (i = 0, limit = target_class_nodeids.size(); i < limit; i++) { + indices.emplace_back( + std::pair(TreeNodeElementId{target_class_treeids[i], target_class_nodeids[i]}, i)); + } + + std::sort(indices.begin(), indices.end()); + // Let's construct nodes_ such that the false branch is always the next element in nodes_. // updated_mapping will translates the old position of each node to the new node position in nodes_. std::vector updated_mapping(nodes_treeids.size(), 0); @@ -280,26 +298,13 @@ Status TreeEnsembleCommon::Init( int64_t tree_id = node_tree_ids[i].tree_id; size_t root_position = AddNodes(i, cmodes, truenode_ids, falsenode_ids, nodes_featureids, nodes_values_as_tensor, nodes_values, - nodes_missing_value_tracks_true, updated_mapping, tree_id, node_tree_ids); + nodes_missing_value_tracks_true, updated_mapping, tree_id, node_tree_ids, + target_class_weights, target_class_weights_as_tensor, indices); roots_.push_back(&nodes_[root_position]); previous_tree_id = tree_id; } } - n_trees_ = roots_.size(); - if (((int64_t)nodes_.size()) != n_nodes_) { - ORT_THROW("Number of nodes in nodes_ (", nodes_.size(), ") is different from n_nodes (", n_nodes_, ")."); - } - - // Sort targets - InlinedVector> indices; - indices.reserve(target_class_nodeids.size()); - for (i = 0, limit = target_class_nodeids.size(); i < limit; i++) { - indices.emplace_back( - std::pair(TreeNodeElementId{target_class_treeids[i], target_class_nodeids[i]}, i)); - } - - std::sort(indices.begin(), indices.end()); TreeNodeElementId ind; SparseValue w; @@ -341,13 +346,59 @@ Status TreeEnsembleCommon::Init( return Status::OK(); } +template +bool TreeEnsembleCommon::CheckIfSubtreesAreEqual( + const size_t left_id, const size_t right_id, const int64_t tree_id, const InlinedVector& cmodes, + const InlinedVector& truenode_ids, const InlinedVector& falsenode_ids, const std::vector& nodes_featureids, + const std::vector& nodes_values_as_tensor, const std::vector& node_values, + const std::vector& target_class_weights, const std::vector& target_class_weights_as_tensor, + const InlinedVector& node_tree_ids, InlinedVector> indices) { + // Leaves have values set at 0 + if (cmodes[left_id] != cmodes[right_id] || nodes_featureids[left_id] != nodes_featureids[right_id] || (!nodes_values_as_tensor.empty() && nodes_values_as_tensor[left_id] != nodes_values_as_tensor[right_id]) || (nodes_values_as_tensor.empty() && node_values[left_id] != node_values[right_id])) { + return false; + } + + if (cmodes[left_id] == NODE_MODE::LEAF) { + auto left_tree_node = node_tree_ids[left_id]; + auto left_target_node = std::lower_bound(indices.begin(), indices.end(), std::make_pair(left_tree_node, uint32_t(0)))->second; + + auto right_tree_node = node_tree_ids[right_id]; + auto right_target_node = std::lower_bound(indices.begin(), indices.end(), std::make_pair(right_tree_node, uint32_t(0)))->second; + + if (target_class_weights_as_tensor.empty()) { + return target_class_weights[left_target_node] == target_class_weights[right_target_node]; + } else { + return target_class_weights_as_tensor[left_target_node] == target_class_weights_as_tensor[right_target_node]; + } + } + + return CheckIfSubtreesAreEqual(falsenode_ids[left_id], falsenode_ids[right_id], tree_id, cmodes, truenode_ids, falsenode_ids, nodes_featureids, + nodes_values_as_tensor, node_values, target_class_weights, target_class_weights_as_tensor, node_tree_ids, indices) && + CheckIfSubtreesAreEqual(truenode_ids[left_id], truenode_ids[right_id], tree_id, cmodes, truenode_ids, falsenode_ids, nodes_featureids, + nodes_values_as_tensor, node_values, target_class_weights, target_class_weights_as_tensor, node_tree_ids, indices); +} + +inline void UpdateThreshold(double val, double& mask) { + uint64_t new_mask = *reinterpret_cast(&mask) | (1ll << (static_cast(val) - 1)); + mask = *reinterpret_cast(&new_mask); +} + +inline void UpdateThreshold(float val, float& mask) { + uint32_t new_mask = *reinterpret_cast(&mask) | (1 << (static_cast(val) - 1)); + mask = *reinterpret_cast(&new_mask); +} + +#define BITCOUNT(T) int64_t(sizeof(T) * 8) +#define CANMASK(v, T) (v >= 1 && v <= BITCOUNT(T)) + template size_t TreeEnsembleCommon::AddNodes( const size_t i, const InlinedVector& cmodes, const InlinedVector& truenode_ids, const InlinedVector& falsenode_ids, const std::vector& nodes_featureids, const std::vector& nodes_values_as_tensor, const std::vector& node_values, const std::vector& nodes_missing_value_tracks_true, std::vector& updated_mapping, int64_t tree_id, - const InlinedVector& node_tree_ids) { + const InlinedVector& node_tree_ids, const std::vector& target_class_weights, + const std::vector& target_class_weights_as_tensor, InlinedVector> indices) { // Validate this index maps to the same tree_id as the one we should be building. if (node_tree_ids[i].tree_id != tree_id) { ORT_THROW("Tree id mismatch. Expected ", tree_id, " but got ", node_tree_ids[i].tree_id, " at position ", i); @@ -369,23 +420,47 @@ size_t TreeEnsembleCommon::AddNodes( if (node.feature_id > max_feature_id_) { max_feature_id_ = node.feature_id; } - node.value_or_unique_weight = - nodes_values_as_tensor.empty() ? static_cast(node_values[i]) : nodes_values_as_tensor[i]; + + node.value_or_unique_weight = 0; + const auto node_threshold = nodes_values_as_tensor.empty() ? static_cast(node_values[i]) : nodes_values_as_tensor[i]; + if (node.flags == NODE_MODE::BRANCH_EQ && CANMASK(node_threshold, ThresholdType)) { + UpdateThreshold(node_threshold, node.value_or_unique_weight); + node.flags = NODE_MODE::BRANCH_SM; + } else { + node.value_or_unique_weight = node_threshold; + } + if (i < static_cast(nodes_missing_value_tracks_true.size()) && nodes_missing_value_tracks_true[i] == 1) { node.flags |= static_cast(MissingTrack::kTrue); } nodes_.push_back(std::move(node)); if (nodes_[node_pos].is_not_leaf()) { + auto falsenode_id = falsenode_ids[i]; + if (nodes_[node_pos].flags == NODE_MODE::BRANCH_SM) { + auto falsenode_threshold = nodes_values_as_tensor.empty() ? static_cast(node_values[falsenode_id]) : nodes_values_as_tensor[falsenode_id]; + + while (cmodes[falsenode_id] == NODE_MODE::BRANCH_EQ && nodes_[node_pos].feature_id == nodes_featureids[falsenode_id] && + CANMASK(falsenode_threshold, ThresholdType) && + CheckIfSubtreesAreEqual(truenode_ids[i], truenode_ids[falsenode_id], tree_id, cmodes, truenode_ids, falsenode_ids, + nodes_featureids, nodes_values_as_tensor, node_values, target_class_weights, target_class_weights_as_tensor, node_tree_ids, indices)) { + UpdateThreshold(falsenode_threshold, nodes_[node_pos].value_or_unique_weight); + falsenode_id = falsenode_ids[falsenode_id]; + falsenode_threshold = nodes_values_as_tensor.empty() ? static_cast(node_values[falsenode_id]) : nodes_values_as_tensor[falsenode_id]; + } + } + size_t false_branch = - AddNodes(falsenode_ids[i], cmodes, truenode_ids, falsenode_ids, nodes_featureids, nodes_values_as_tensor, - node_values, nodes_missing_value_tracks_true, updated_mapping, tree_id, node_tree_ids); + AddNodes(falsenode_id, cmodes, truenode_ids, falsenode_ids, nodes_featureids, nodes_values_as_tensor, + node_values, nodes_missing_value_tracks_true, updated_mapping, tree_id, node_tree_ids, + target_class_weights, target_class_weights_as_tensor, indices); if (false_branch != node_pos + 1) { ORT_THROW("False node must always be the next node, but it isn't at index ", node_pos, " with flags ", static_cast(nodes_[node_pos].flags)); } size_t true_branch = AddNodes(truenode_ids[i], cmodes, truenode_ids, falsenode_ids, nodes_featureids, nodes_values_as_tensor, - node_values, nodes_missing_value_tracks_true, updated_mapping, tree_id, node_tree_ids); + node_values, nodes_missing_value_tracks_true, updated_mapping, tree_id, node_tree_ids, + target_class_weights, target_class_weights_as_tensor, indices); // We don't need to store the false branch pointer since we know it is always in the immediate next entry in nodes_. // nodes_[node_pos].falsenode_inc_or_n_weights.ptr = &nodes_[false_branch]; nodes_[node_pos].truenode_or_weight.ptr = &nodes_[true_branch]; @@ -684,6 +759,16 @@ void TreeEnsembleCommon::ComputeAgg(concur } \ } +inline bool SetMembershipCheck(double val, double mask) { + auto val_as_int = static_cast(val); + return CANMASK(val_as_int, double) && (((1ll << (val_as_int - 1)) & *reinterpret_cast(&mask)) != 0); +} + +inline bool SetMembershipCheck(float val, float mask) { + auto val_as_int = static_cast(val); + return CANMASK(val_as_int, float) && (((1ll << (val_as_int - 1)) & *reinterpret_cast(&mask)) != 0); +} + inline bool _isnan_(float x) { return std::isnan(x); } inline bool _isnan_(double x) { return std::isnan(x); } inline bool _isnan_(int64_t) { return false; } @@ -726,6 +811,20 @@ TreeEnsembleCommon::ProcessTreeNodeLeave( case NODE_MODE::BRANCH_NEQ: TREE_FIND_VALUE(!=) break; + case NODE_MODE::BRANCH_SM: + if (has_missing_tracks_) { + while (root->is_not_leaf()) { + val = x_data[root->feature_id]; + root = (SetMembershipCheck(val, root->value_or_unique_weight) || (root->is_missing_track_true() && _isnan_(val))) + ? root->truenode_or_weight.ptr + : root + 1; + } + } else { + while (root->is_not_leaf()) { + val = x_data[root->feature_id]; + root = SetMembershipCheck(val, root->value_or_unique_weight) ? root->truenode_or_weight.ptr : root + 1; + } + } case NODE_MODE::LEAF: break; } @@ -759,6 +858,11 @@ TreeEnsembleCommon::ProcessTreeNodeLeave( root = val != threshold || (root->is_missing_track_true() && _isnan_(val)) ? root->truenode_or_weight.ptr : root + 1; break; + case NODE_MODE::BRANCH_SM: + root = (SetMembershipCheck(val, root->value_or_unique_weight) || (root->is_missing_track_true() && _isnan_(val))) + ? root->truenode_or_weight.ptr + : root + 1; + break; case NODE_MODE::LEAF: return root; } diff --git a/onnxruntime/test/providers/cpu/ml/treeregressor_test.cc b/onnxruntime/test/providers/cpu/ml/treeregressor_test.cc index 33c23b53fb5aa..eaf8fea03eaa0 100644 --- a/onnxruntime/test/providers/cpu/ml/treeregressor_test.cc +++ b/onnxruntime/test/providers/cpu/ml/treeregressor_test.cc @@ -679,6 +679,90 @@ TEST(MLOpTest, TreeRegressorSingleTargetSum_as_tensor_precision) { GenTreeAndRunTest1_as_tensor_precision(3); } +TEST(MLOpTest, TreeRegressorCategoricals) { + OpTester test("TreeEnsembleRegressor", 3, onnxruntime::kMLDomain); + + // tree + int64_t n_targets = 1; + std::vector nodes_featureids = {0, 0, 0, 0, 1, 0, 0}; + std::vector nodes_modes = {"BRANCH_EQ", "BRANCH_EQ", "BRANCH_EQ", "LEAF", "BRANCH_LEQ", "LEAF", "LEAF"}; + std::vector nodes_values = {1, 3, 4, 0, 5.5, 0, 0}; + + std::vector nodes_treeids = {0, 0, 0, 0, 0, 0, 0}; + std::vector nodes_nodeids = {0, 1, 2, 3, 4, 5, 6}; + std::vector nodes_falsenodeids = {1, 2, 3, 0, 5, 0, 0}; + std::vector nodes_truenodeids = {4, 4, 4, 0, 6, 0, 0}; + + std::string post_transform = "NONE"; + std::vector target_ids = {0, 0, 0}; + std::vector target_nodeids = {3, 5, 6}; + std::vector target_treeids = {0, 0, 0}; + std::vector target_weights = {-4.699999809265137, 17.700000762939453, 11.100000381469727}; + + // add attributes + test.AddAttribute("nodes_truenodeids", nodes_truenodeids); + test.AddAttribute("nodes_falsenodeids", nodes_falsenodeids); + test.AddAttribute("nodes_treeids", nodes_treeids); + test.AddAttribute("nodes_nodeids", nodes_nodeids); + test.AddAttribute("nodes_featureids", nodes_featureids); + test.AddAttribute("nodes_values", nodes_values); + test.AddAttribute("nodes_modes", nodes_modes); + test.AddAttribute("target_treeids", target_treeids); + test.AddAttribute("target_nodeids", target_nodeids); + test.AddAttribute("target_ids", target_ids); + test.AddAttribute("target_weights", target_weights); + test.AddAttribute("n_targets", n_targets); + + // fill input data + std::vector X = {3.0f, 6.6f, 1.0f, 5.0f, 5.0f, 5.5f}; + std::vector Y = {17.700000762939453, 11.100000381469727, -4.699999809265137}; + test.AddInput("X", {3, 2}, X); + test.AddOutput("Y", {3, 1}, Y); + test.Run(); +} + +TEST(MLOpTest, TreeRegressorCategoricalsFolding) { + OpTester test("TreeEnsembleRegressor", 3, onnxruntime::kMLDomain); + + // tree + int64_t n_targets = 1; + std::vector nodes_featureids = {0, 0, 1, 1, 0, 0, 0}; + std::vector nodes_modes = {"BRANCH_EQ", "BRANCH_EQ", "BRANCH_EQ", "BRANCH_EQ", "LEAF", "LEAF", "LEAF"}; + std::vector nodes_values = {1, 3, 2, 3, 0, 0, 0}; + + std::vector nodes_treeids = {0, 0, 0, 0, 0, 0, 0}; + std::vector nodes_nodeids = {0, 1, 2, 3, 4, 5, 6}; + std::vector nodes_falsenodeids = {1, 2, 3, 4, 0, 0, 0}; + std::vector nodes_truenodeids = {5, 5, 6, 6, 0, 0, 0}; + + std::string post_transform = "NONE"; + std::vector target_ids = {0, 0, 0}; + std::vector target_nodeids = {4, 5, 6}; + std::vector target_treeids = {0, 0, 0}; + std::vector target_weights = {17.700000762939453, 11.100000381469727, -4.699999809265137}; + + // add attributes + test.AddAttribute("nodes_truenodeids", nodes_truenodeids); + test.AddAttribute("nodes_falsenodeids", nodes_falsenodeids); + test.AddAttribute("nodes_treeids", nodes_treeids); + test.AddAttribute("nodes_nodeids", nodes_nodeids); + test.AddAttribute("nodes_featureids", nodes_featureids); + test.AddAttribute("nodes_values", nodes_values); + test.AddAttribute("nodes_modes", nodes_modes); + test.AddAttribute("target_treeids", target_treeids); + test.AddAttribute("target_nodeids", target_nodeids); + test.AddAttribute("target_ids", target_ids); + test.AddAttribute("target_weights", target_weights); + test.AddAttribute("n_targets", n_targets); + + // fill input data + std::vector X = {1.0f, 2.0f, 3.0f, 1.0f, 2.0f, 3.0f, 2.0f, 1.0f}; + std::vector Y = {11.100000381469727, 11.100000381469727, -4.699999809265137, 17.700000762939453}; + test.AddInput("X", {4, 2}, X); + test.AddOutput("Y", {4, 1}, Y); + test.Run(); +} + TEST(MLOpTest, TreeRegressorTrueNodeBeforeNode) { OpTester test("TreeEnsembleRegressor", 3, onnxruntime::kMLDomain); From 6bd62ad249a5f69ee9d620100bfbd775d904806b Mon Sep 17 00:00:00 2001 From: Bilyana Indzheva Date: Sun, 18 Aug 2024 19:31:49 +0300 Subject: [PATCH 02/10] Change reinterpret_cast to bit_cast as it is safer --- .vscode/settings.json | 80 ++++++++++++++++++- .../providers/cpu/ml/tree_ensemble_common.h | 22 +++-- 2 files changed, 93 insertions(+), 9 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 98d23090fd474..c9906bf634747 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -23,6 +23,84 @@ "-runtime/references" ], "files.associations": { - "span": "cpp" + "span": "cpp", + "__locale": "cpp", + "__bit_reference": "cpp", + "__config": "cpp", + "__debug": "cpp", + "__errc": "cpp", + "__hash_table": "cpp", + "__mutex_base": "cpp", + "__node_handle": "cpp", + "__split_buffer": "cpp", + "__threading_support": "cpp", + "__tree": "cpp", + "__verbose_abort": "cpp", + "array": "cpp", + "atomic": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "cfenv": "cpp", + "charconv": "cpp", + "cinttypes": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "codecvt": "cpp", + "complex": "cpp", + "condition_variable": "cpp", + "csignal": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "exception": "cpp", + "fstream": "cpp", + "future": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "list": "cpp", + "locale": "cpp", + "map": "cpp", + "mutex": "cpp", + "new": "cpp", + "optional": "cpp", + "ostream": "cpp", + "queue": "cpp", + "ratio": "cpp", + "regex": "cpp", + "set": "cpp", + "sstream": "cpp", + "stack": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "string": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "thread": "cpp", + "tuple": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "variant": "cpp", + "vector": "cpp", + "algorithm": "cpp", + "pointers": "cpp", + "any": "cpp", + "forward_list": "cpp", + "scoped_allocator": "cpp", + "typeindex": "cpp", + "valarray": "cpp", + "*.inc": "cpp" } } diff --git a/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h b/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h index aba22afdf5694..419c8d01f1fd9 100644 --- a/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h +++ b/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h @@ -8,8 +8,6 @@ #include "core/platform/threadpool.h" #include "tree_ensemble_helper.h" -#include - namespace onnxruntime { namespace ml { namespace detail { @@ -379,13 +377,13 @@ bool TreeEnsembleCommon::CheckIfSubtreesAr } inline void UpdateThreshold(double val, double& mask) { - uint64_t new_mask = *reinterpret_cast(&mask) | (1ll << (static_cast(val) - 1)); - mask = *reinterpret_cast(&new_mask); + uint64_t new_mask = std::bit_cast(mask) | (1ll << (static_cast(val) - 1)); + mask = std::bit_cast(new_mask); } inline void UpdateThreshold(float val, float& mask) { - uint32_t new_mask = *reinterpret_cast(&mask) | (1 << (static_cast(val) - 1)); - mask = *reinterpret_cast(&new_mask); + uint32_t new_mask = std::bit_cast(mask) | (1 << (static_cast(val) - 1)); + mask = std::bit_cast(new_mask); } #define BITCOUNT(T) int64_t(sizeof(T) * 8) @@ -436,6 +434,13 @@ size_t TreeEnsembleCommon::AddNodes( nodes_.push_back(std::move(node)); if (nodes_[node_pos].is_not_leaf()) { auto falsenode_id = falsenode_ids[i]; + + // Categoricals are represented as a chain of `EQ` nodes where the subtree for the true child is identical for all nodes in the chain + // Below we are folding together these nodes into one of mode `BRANCH_SM` + // The threshold of this node should be looked as a bitmask showing which categoricals values were found in the chain + // Afterwards, when looking whether a feature is included we can do an `and` with the mask of the node + // and the one of the feature (the mask has only one bit set on the place for its value) + // Beware that if a category is bigger than the threshold type, the node stays as `EQ` and no combination is done if (nodes_[node_pos].flags == NODE_MODE::BRANCH_SM) { auto falsenode_threshold = nodes_values_as_tensor.empty() ? static_cast(node_values[falsenode_id]) : nodes_values_as_tensor[falsenode_id]; @@ -759,14 +764,15 @@ void TreeEnsembleCommon::ComputeAgg(concur } \ } +// Check whether the feature value is set true in the mask inline bool SetMembershipCheck(double val, double mask) { auto val_as_int = static_cast(val); - return CANMASK(val_as_int, double) && (((1ll << (val_as_int - 1)) & *reinterpret_cast(&mask)) != 0); + return CANMASK(val_as_int, double) && (((1ll << (val_as_int - 1)) & std::bit_cast(mask)) != 0); } inline bool SetMembershipCheck(float val, float mask) { auto val_as_int = static_cast(val); - return CANMASK(val_as_int, float) && (((1ll << (val_as_int - 1)) & *reinterpret_cast(&mask)) != 0); + return CANMASK(val_as_int, float) && (((1ll << (val_as_int - 1)) & std::bit_cast(mask)) != 0); } inline bool _isnan_(float x) { return std::isnan(x); } From 249a327774f0e927c0faa7b269fbca6a6b5c1207 Mon Sep 17 00:00:00 2001 From: Bilyana Indzheva Date: Mon, 19 Aug 2024 16:22:34 +0300 Subject: [PATCH 03/10] not use std::bit_cast as it is not supported in c++20 --- .../providers/cpu/ml/tree_ensemble_common.h | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h b/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h index 419c8d01f1fd9..044c639cad3b4 100644 --- a/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h +++ b/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h @@ -100,6 +100,26 @@ class TreeEnsembleCommon : public TreeEnsembleCommonAttributes { const std::vector& target_class_weights_as_tensor, InlinedVector> indices); }; +// Below is simple implementation of `bit_cast` as it is supported from c++20 and the current supported version is c++17 +// Remove it when that is not the case +template +std::enable_if_t< + sizeof(To) == sizeof(From) && + std::is_trivially_copyable_v && + std::is_trivially_copyable_v, + To> +// constexpr support needs compiler magic +static bit_cast(const From& src) noexcept +{ + static_assert(std::is_trivially_constructible_v, + "This implementation additionally requires " + "destination type to be trivially constructible"); + + To dst; + std::memcpy(&dst, &src, sizeof(To)); + return dst; +} + template Status TreeEnsembleCommon::Init(const OpKernelInfo& info) { std::vector base_values_as_tensor, nodes_hitrates_as_tensor, @@ -377,13 +397,13 @@ bool TreeEnsembleCommon::CheckIfSubtreesAr } inline void UpdateThreshold(double val, double& mask) { - uint64_t new_mask = std::bit_cast(mask) | (1ll << (static_cast(val) - 1)); - mask = std::bit_cast(new_mask); + uint64_t new_mask = bit_cast(mask) | (1ll << (static_cast(val) - 1)); + mask = bit_cast(new_mask); } inline void UpdateThreshold(float val, float& mask) { - uint32_t new_mask = std::bit_cast(mask) | (1 << (static_cast(val) - 1)); - mask = std::bit_cast(new_mask); + uint32_t new_mask = bit_cast(mask) | (1 << (static_cast(val) - 1)); + mask = bit_cast(new_mask); } #define BITCOUNT(T) int64_t(sizeof(T) * 8) @@ -437,7 +457,7 @@ size_t TreeEnsembleCommon::AddNodes( // Categoricals are represented as a chain of `EQ` nodes where the subtree for the true child is identical for all nodes in the chain // Below we are folding together these nodes into one of mode `BRANCH_SM` - // The threshold of this node should be looked as a bitmask showing which categoricals values were found in the chain + // The threshold of this node should be interpreted as a bitmask showing which categoricals values were found in the chain // Afterwards, when looking whether a feature is included we can do an `and` with the mask of the node // and the one of the feature (the mask has only one bit set on the place for its value) // Beware that if a category is bigger than the threshold type, the node stays as `EQ` and no combination is done @@ -767,12 +787,12 @@ void TreeEnsembleCommon::ComputeAgg(concur // Check whether the feature value is set true in the mask inline bool SetMembershipCheck(double val, double mask) { auto val_as_int = static_cast(val); - return CANMASK(val_as_int, double) && (((1ll << (val_as_int - 1)) & std::bit_cast(mask)) != 0); + return CANMASK(val_as_int, double) && (((1ll << (val_as_int - 1)) & bit_cast(mask)) != 0); } inline bool SetMembershipCheck(float val, float mask) { auto val_as_int = static_cast(val); - return CANMASK(val_as_int, float) && (((1ll << (val_as_int - 1)) & std::bit_cast(mask)) != 0); + return CANMASK(val_as_int, float) && (((1ll << (val_as_int - 1)) & bit_cast(mask)) != 0); } inline bool _isnan_(float x) { return std::isnan(x); } From b0e8c80e53498aaf05dd1f460dff77b56b4e5ee1 Mon Sep 17 00:00:00 2001 From: Bilyana Indzheva Date: Mon, 19 Aug 2024 16:28:43 +0300 Subject: [PATCH 04/10] fix --- .vscode/settings.json | 80 +------------------------------------------ 1 file changed, 1 insertion(+), 79 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index c9906bf634747..98d23090fd474 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -23,84 +23,6 @@ "-runtime/references" ], "files.associations": { - "span": "cpp", - "__locale": "cpp", - "__bit_reference": "cpp", - "__config": "cpp", - "__debug": "cpp", - "__errc": "cpp", - "__hash_table": "cpp", - "__mutex_base": "cpp", - "__node_handle": "cpp", - "__split_buffer": "cpp", - "__threading_support": "cpp", - "__tree": "cpp", - "__verbose_abort": "cpp", - "array": "cpp", - "atomic": "cpp", - "bitset": "cpp", - "cctype": "cpp", - "cfenv": "cpp", - "charconv": "cpp", - "cinttypes": "cpp", - "clocale": "cpp", - "cmath": "cpp", - "codecvt": "cpp", - "complex": "cpp", - "condition_variable": "cpp", - "csignal": "cpp", - "cstdarg": "cpp", - "cstddef": "cpp", - "cstdint": "cpp", - "cstdio": "cpp", - "cstdlib": "cpp", - "cstring": "cpp", - "ctime": "cpp", - "cwchar": "cpp", - "cwctype": "cpp", - "deque": "cpp", - "exception": "cpp", - "fstream": "cpp", - "future": "cpp", - "initializer_list": "cpp", - "iomanip": "cpp", - "ios": "cpp", - "iosfwd": "cpp", - "iostream": "cpp", - "istream": "cpp", - "limits": "cpp", - "list": "cpp", - "locale": "cpp", - "map": "cpp", - "mutex": "cpp", - "new": "cpp", - "optional": "cpp", - "ostream": "cpp", - "queue": "cpp", - "ratio": "cpp", - "regex": "cpp", - "set": "cpp", - "sstream": "cpp", - "stack": "cpp", - "stdexcept": "cpp", - "streambuf": "cpp", - "string": "cpp", - "string_view": "cpp", - "system_error": "cpp", - "thread": "cpp", - "tuple": "cpp", - "typeinfo": "cpp", - "unordered_map": "cpp", - "unordered_set": "cpp", - "variant": "cpp", - "vector": "cpp", - "algorithm": "cpp", - "pointers": "cpp", - "any": "cpp", - "forward_list": "cpp", - "scoped_allocator": "cpp", - "typeindex": "cpp", - "valarray": "cpp", - "*.inc": "cpp" + "span": "cpp" } } From 921142857f14b5c9ab82078117c7afb3890229c6 Mon Sep 17 00:00:00 2001 From: Bilyana Indzheva Date: Tue, 27 Aug 2024 15:02:03 +0300 Subject: [PATCH 05/10] Renaming --- onnxruntime/core/providers/cpu/ml/ml_common.h | 6 +++--- .../core/providers/cpu/ml/tree_ensemble_common.h | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/onnxruntime/core/providers/cpu/ml/ml_common.h b/onnxruntime/core/providers/cpu/ml/ml_common.h index 82a38691b58e5..fe6504426e6ad 100644 --- a/onnxruntime/core/providers/cpu/ml/ml_common.h +++ b/onnxruntime/core/providers/cpu/ml/ml_common.h @@ -28,7 +28,7 @@ enum NODE_MODE : uint8_t { BRANCH_GT = 8, BRANCH_EQ = 10, BRANCH_NEQ = 12, - BRANCH_SM = 14 + BRANCH_MEMBER = 14 }; static inline NODE_MODE MakeTreeNodeMode(const std::string& input) { @@ -50,8 +50,8 @@ static inline NODE_MODE MakeTreeNodeMode(const std::string& input) { if (input == "BRANCH_EQ") { return NODE_MODE::BRANCH_EQ; } - if (input == "BRANCH_SM") { - return NODE_MODE::BRANCH_SM; + if (input == "BRANCH_MEMBER") { + return NODE_MODE::BRANCH_MEMBER; } return NODE_MODE::BRANCH_NEQ; } diff --git a/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h b/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h index 044c639cad3b4..6cb23e05a3fbe 100644 --- a/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h +++ b/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h @@ -443,7 +443,7 @@ size_t TreeEnsembleCommon::AddNodes( const auto node_threshold = nodes_values_as_tensor.empty() ? static_cast(node_values[i]) : nodes_values_as_tensor[i]; if (node.flags == NODE_MODE::BRANCH_EQ && CANMASK(node_threshold, ThresholdType)) { UpdateThreshold(node_threshold, node.value_or_unique_weight); - node.flags = NODE_MODE::BRANCH_SM; + node.flags = NODE_MODE::BRANCH_MEMBER; } else { node.value_or_unique_weight = node_threshold; } @@ -456,12 +456,12 @@ size_t TreeEnsembleCommon::AddNodes( auto falsenode_id = falsenode_ids[i]; // Categoricals are represented as a chain of `EQ` nodes where the subtree for the true child is identical for all nodes in the chain - // Below we are folding together these nodes into one of mode `BRANCH_SM` + // Below we are folding together these nodes into one of mode `BRANCH_MEMBER` // The threshold of this node should be interpreted as a bitmask showing which categoricals values were found in the chain // Afterwards, when looking whether a feature is included we can do an `and` with the mask of the node // and the one of the feature (the mask has only one bit set on the place for its value) // Beware that if a category is bigger than the threshold type, the node stays as `EQ` and no combination is done - if (nodes_[node_pos].flags == NODE_MODE::BRANCH_SM) { + if (nodes_[node_pos].flags == NODE_MODE::BRANCH_MEMBER) { auto falsenode_threshold = nodes_values_as_tensor.empty() ? static_cast(node_values[falsenode_id]) : nodes_values_as_tensor[falsenode_id]; while (cmodes[falsenode_id] == NODE_MODE::BRANCH_EQ && nodes_[node_pos].feature_id == nodes_featureids[falsenode_id] && @@ -837,7 +837,7 @@ TreeEnsembleCommon::ProcessTreeNodeLeave( case NODE_MODE::BRANCH_NEQ: TREE_FIND_VALUE(!=) break; - case NODE_MODE::BRANCH_SM: + case NODE_MODE::BRANCH_MEMBER: if (has_missing_tracks_) { while (root->is_not_leaf()) { val = x_data[root->feature_id]; @@ -884,7 +884,7 @@ TreeEnsembleCommon::ProcessTreeNodeLeave( root = val != threshold || (root->is_missing_track_true() && _isnan_(val)) ? root->truenode_or_weight.ptr : root + 1; break; - case NODE_MODE::BRANCH_SM: + case NODE_MODE::BRANCH_MEMBER: root = (SetMembershipCheck(val, root->value_or_unique_weight) || (root->is_missing_track_true() && _isnan_(val))) ? root->truenode_or_weight.ptr : root + 1; From b2ba77df30dca947926ba9972d289ecc26a3c848 Mon Sep 17 00:00:00 2001 From: Bilyana Indzheva Date: Wed, 28 Aug 2024 17:44:02 +0300 Subject: [PATCH 06/10] Formatting --- .../providers/cpu/ml/tree_ensemble_common.h | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h b/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h index 6cb23e05a3fbe..afd2c4e5230bf 100644 --- a/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h +++ b/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h @@ -102,22 +102,21 @@ class TreeEnsembleCommon : public TreeEnsembleCommonAttributes { // Below is simple implementation of `bit_cast` as it is supported from c++20 and the current supported version is c++17 // Remove it when that is not the case -template +template std::enable_if_t< sizeof(To) == sizeof(From) && - std::is_trivially_copyable_v && - std::is_trivially_copyable_v, + std::is_trivially_copyable_v && + std::is_trivially_copyable_v, To> -// constexpr support needs compiler magic -static bit_cast(const From& src) noexcept -{ - static_assert(std::is_trivially_constructible_v, - "This implementation additionally requires " - "destination type to be trivially constructible"); - - To dst; - std::memcpy(&dst, &src, sizeof(To)); - return dst; + // constexpr support needs compiler magic + static bit_cast(const From& src) noexcept { + static_assert(std::is_trivially_constructible_v, + "This implementation additionally requires " + "destination type to be trivially constructible"); + + To dst; + std::memcpy(&dst, &src, sizeof(To)); + return dst; } template From d54b084a5e9b5143794fdc86fe7393de5c1a3f2a Mon Sep 17 00:00:00 2001 From: Bilyana Indzheva Date: Wed, 28 Aug 2024 18:15:26 +0300 Subject: [PATCH 07/10] small changes --- .../core/providers/cpu/ml/tree_ensemble_common.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h b/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h index afd2c4e5230bf..d700acf9de15a 100644 --- a/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h +++ b/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h @@ -300,7 +300,7 @@ Status TreeEnsembleCommon::Init( indices.reserve(target_class_nodeids.size()); for (i = 0, limit = target_class_nodeids.size(); i < limit; i++) { indices.emplace_back( - std::pair(TreeNodeElementId{target_class_treeids[i], target_class_nodeids[i]}, i)); + TreeNodeElementId{target_class_treeids[i], target_class_nodeids[i]}, i); } std::sort(indices.begin(), indices.end()); @@ -376,11 +376,11 @@ bool TreeEnsembleCommon::CheckIfSubtreesAr } if (cmodes[left_id] == NODE_MODE::LEAF) { - auto left_tree_node = node_tree_ids[left_id]; - auto left_target_node = std::lower_bound(indices.begin(), indices.end(), std::make_pair(left_tree_node, uint32_t(0)))->second; + const auto left_tree_node = node_tree_ids[left_id]; + const auto left_target_node = std::lower_bound(indices.begin(), indices.end(), std::make_pair(left_tree_node, uint32_t(0)))->second; - auto right_tree_node = node_tree_ids[right_id]; - auto right_target_node = std::lower_bound(indices.begin(), indices.end(), std::make_pair(right_tree_node, uint32_t(0)))->second; + const auto right_tree_node = node_tree_ids[right_id]; + const auto right_target_node = std::lower_bound(indices.begin(), indices.end(), std::make_pair(right_tree_node, uint32_t(0)))->second; if (target_class_weights_as_tensor.empty()) { return target_class_weights[left_target_node] == target_class_weights[right_target_node]; @@ -406,7 +406,7 @@ inline void UpdateThreshold(float val, float& mask) { } #define BITCOUNT(T) int64_t(sizeof(T) * 8) -#define CANMASK(v, T) (v >= 1 && v <= BITCOUNT(T)) +#define CANMASK(v, T) (v >= 1 && v <= BITCOUNT(T)) && v == std::floor(v) template size_t TreeEnsembleCommon::AddNodes( @@ -785,12 +785,12 @@ void TreeEnsembleCommon::ComputeAgg(concur // Check whether the feature value is set true in the mask inline bool SetMembershipCheck(double val, double mask) { - auto val_as_int = static_cast(val); + const auto val_as_int = static_cast(val); return CANMASK(val_as_int, double) && (((1ll << (val_as_int - 1)) & bit_cast(mask)) != 0); } inline bool SetMembershipCheck(float val, float mask) { - auto val_as_int = static_cast(val); + const auto val_as_int = static_cast(val); return CANMASK(val_as_int, float) && (((1ll << (val_as_int - 1)) & bit_cast(mask)) != 0); } From 1b15a1d529ae9e3c483dfdf5c310f0d7287aeea9 Mon Sep 17 00:00:00 2001 From: Bilyana Indzheva Date: Wed, 18 Sep 2024 18:53:58 +0300 Subject: [PATCH 08/10] Fix windows error --- .../providers/cpu/ml/tree_ensemble_common.h | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h b/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h index d700acf9de15a..7bf2552a8a56d 100644 --- a/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h +++ b/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h @@ -119,6 +119,17 @@ std::enable_if_t< return dst; } +template +std::conditional_t bit_cast_int(T val) { + if constexpr (sizeof(T) == sizeof(uint32_t)) { + return bit_cast(val); + } + else if constexpr (sizeof(T) == sizeof(uint64_t)) { + return bit_cast(val); + } + static_assert(sizeof(T) == sizeof(uint32_t) || sizeof(T) == sizeof(uint64_t)); +} + template Status TreeEnsembleCommon::Init(const OpKernelInfo& info) { std::vector base_values_as_tensor, nodes_hitrates_as_tensor, @@ -376,11 +387,8 @@ bool TreeEnsembleCommon::CheckIfSubtreesAr } if (cmodes[left_id] == NODE_MODE::LEAF) { - const auto left_tree_node = node_tree_ids[left_id]; - const auto left_target_node = std::lower_bound(indices.begin(), indices.end(), std::make_pair(left_tree_node, uint32_t(0)))->second; - - const auto right_tree_node = node_tree_ids[right_id]; - const auto right_target_node = std::lower_bound(indices.begin(), indices.end(), std::make_pair(right_tree_node, uint32_t(0)))->second; + const auto left_target_node = std::lower_bound(indices.begin(), indices.end(), std::make_pair(node_tree_ids[left_id], uint32_t(0)))->second; + const auto right_target_node = std::lower_bound(indices.begin(), indices.end(), std::make_pair(node_tree_ids[right_id], uint32_t(0)))->second; if (target_class_weights_as_tensor.empty()) { return target_class_weights[left_target_node] == target_class_weights[right_target_node]; @@ -439,7 +447,7 @@ size_t TreeEnsembleCommon::AddNodes( } node.value_or_unique_weight = 0; - const auto node_threshold = nodes_values_as_tensor.empty() ? static_cast(node_values[i]) : nodes_values_as_tensor[i]; + const ThresholdType node_threshold = nodes_values_as_tensor.empty() ? static_cast(node_values[i]) : nodes_values_as_tensor[i]; if (node.flags == NODE_MODE::BRANCH_EQ && CANMASK(node_threshold, ThresholdType)) { UpdateThreshold(node_threshold, node.value_or_unique_weight); node.flags = NODE_MODE::BRANCH_MEMBER; @@ -452,7 +460,7 @@ size_t TreeEnsembleCommon::AddNodes( } nodes_.push_back(std::move(node)); if (nodes_[node_pos].is_not_leaf()) { - auto falsenode_id = falsenode_ids[i]; + size_t falsenode_id = falsenode_ids[i]; // Categoricals are represented as a chain of `EQ` nodes where the subtree for the true child is identical for all nodes in the chain // Below we are folding together these nodes into one of mode `BRANCH_MEMBER` @@ -461,7 +469,7 @@ size_t TreeEnsembleCommon::AddNodes( // and the one of the feature (the mask has only one bit set on the place for its value) // Beware that if a category is bigger than the threshold type, the node stays as `EQ` and no combination is done if (nodes_[node_pos].flags == NODE_MODE::BRANCH_MEMBER) { - auto falsenode_threshold = nodes_values_as_tensor.empty() ? static_cast(node_values[falsenode_id]) : nodes_values_as_tensor[falsenode_id]; + ThresholdType falsenode_threshold = nodes_values_as_tensor.empty() ? static_cast(node_values[falsenode_id]) : nodes_values_as_tensor[falsenode_id]; while (cmodes[falsenode_id] == NODE_MODE::BRANCH_EQ && nodes_[node_pos].feature_id == nodes_featureids[falsenode_id] && CANMASK(falsenode_threshold, ThresholdType) && @@ -783,16 +791,15 @@ void TreeEnsembleCommon::ComputeAgg(concur } \ } + + // Check whether the feature value is set true in the mask -inline bool SetMembershipCheck(double val, double mask) { - const auto val_as_int = static_cast(val); - return CANMASK(val_as_int, double) && (((1ll << (val_as_int - 1)) & bit_cast(mask)) != 0); +template +inline bool SetMembershipCheck(T1 val, T2 mask) { + const int64_t val_as_int = static_cast(val); + return CANMASK(val, T2) && (((1ll << (val_as_int - 1)) & bit_cast_int(mask)) != 0); } -inline bool SetMembershipCheck(float val, float mask) { - const auto val_as_int = static_cast(val); - return CANMASK(val_as_int, float) && (((1ll << (val_as_int - 1)) & bit_cast(mask)) != 0); -} inline bool _isnan_(float x) { return std::isnan(x); } inline bool _isnan_(double x) { return std::isnan(x); } From 11c3d385c6988995a664b0ac2888456209edf31b Mon Sep 17 00:00:00 2001 From: Bilyana Indzheva Date: Wed, 18 Sep 2024 18:56:40 +0300 Subject: [PATCH 09/10] Fix windows error --- onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h b/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h index 7bf2552a8a56d..48caa43622731 100644 --- a/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h +++ b/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h @@ -123,8 +123,7 @@ template std::conditional_t bit_cast_int(T val) { if constexpr (sizeof(T) == sizeof(uint32_t)) { return bit_cast(val); - } - else if constexpr (sizeof(T) == sizeof(uint64_t)) { + } else if constexpr (sizeof(T) == sizeof(uint64_t)) { return bit_cast(val); } static_assert(sizeof(T) == sizeof(uint32_t) || sizeof(T) == sizeof(uint64_t)); @@ -791,8 +790,6 @@ void TreeEnsembleCommon::ComputeAgg(concur } \ } - - // Check whether the feature value is set true in the mask template inline bool SetMembershipCheck(T1 val, T2 mask) { @@ -800,7 +797,6 @@ inline bool SetMembershipCheck(T1 val, T2 mask) { return CANMASK(val, T2) && (((1ll << (val_as_int - 1)) & bit_cast_int(mask)) != 0); } - inline bool _isnan_(float x) { return std::isnan(x); } inline bool _isnan_(double x) { return std::isnan(x); } inline bool _isnan_(int64_t) { return false; } From 91f061e5f7034c4f4bb3474f8a2b678d69bc0e0f Mon Sep 17 00:00:00 2001 From: Bilyana Indzheva Date: Thu, 10 Oct 2024 14:47:12 +0200 Subject: [PATCH 10/10] Add & to indices --- onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h b/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h index 48caa43622731..29e53d1208183 100644 --- a/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h +++ b/onnxruntime/core/providers/cpu/ml/tree_ensemble_common.h @@ -91,13 +91,13 @@ class TreeEnsembleCommon : public TreeEnsembleCommonAttributes { const InlinedVector& truenode_ids, const InlinedVector& falsenode_ids, const std::vector& nodes_featureids, const std::vector& nodes_values_as_tensor, const std::vector& node_values, const std::vector& target_class_weights, const std::vector& target_class_weights_as_tensor, - const InlinedVector& node_tree_ids, InlinedVector> indices); + const InlinedVector& node_tree_ids, InlinedVector>& indices); size_t AddNodes(const size_t i, const InlinedVector& cmodes, const InlinedVector& truenode_ids, const InlinedVector& falsenode_ids, const std::vector& nodes_featureids, const std::vector& nodes_values_as_tensor, const std::vector& node_values, const std::vector& nodes_missing_value_tracks_true, std::vector& updated_mapping, int64_t tree_id, const InlinedVector& node_tree_ids, const std::vector& target_class_weights, - const std::vector& target_class_weights_as_tensor, InlinedVector> indices); + const std::vector& target_class_weights_as_tensor, InlinedVector>& indices); }; // Below is simple implementation of `bit_cast` as it is supported from c++20 and the current supported version is c++17 @@ -379,7 +379,7 @@ bool TreeEnsembleCommon::CheckIfSubtreesAr const InlinedVector& truenode_ids, const InlinedVector& falsenode_ids, const std::vector& nodes_featureids, const std::vector& nodes_values_as_tensor, const std::vector& node_values, const std::vector& target_class_weights, const std::vector& target_class_weights_as_tensor, - const InlinedVector& node_tree_ids, InlinedVector> indices) { + const InlinedVector& node_tree_ids, InlinedVector>& indices) { // Leaves have values set at 0 if (cmodes[left_id] != cmodes[right_id] || nodes_featureids[left_id] != nodes_featureids[right_id] || (!nodes_values_as_tensor.empty() && nodes_values_as_tensor[left_id] != nodes_values_as_tensor[right_id]) || (nodes_values_as_tensor.empty() && node_values[left_id] != node_values[right_id])) { return false; @@ -422,7 +422,7 @@ size_t TreeEnsembleCommon::AddNodes( const std::vector& nodes_values_as_tensor, const std::vector& node_values, const std::vector& nodes_missing_value_tracks_true, std::vector& updated_mapping, int64_t tree_id, const InlinedVector& node_tree_ids, const std::vector& target_class_weights, - const std::vector& target_class_weights_as_tensor, InlinedVector> indices) { + const std::vector& target_class_weights_as_tensor, InlinedVector>& indices) { // Validate this index maps to the same tree_id as the one we should be building. if (node_tree_ids[i].tree_id != tree_id) { ORT_THROW("Tree id mismatch. Expected ", tree_id, " but got ", node_tree_ids[i].tree_id, " at position ", i);