Loading [MathJax]/extensions/tex2jax.js
cuML C++ API  23.12
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
node.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2023, NVIDIA CORPORATION.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #pragma once
20 #include <iostream>
21 #include <type_traits>
22 
23 namespace ML {
24 namespace experimental {
25 namespace fil {
26 
27 namespace detail {
28 
29 /*
30  * Return the byte size to which a node with the given types should be aligned
31  */
32 template <typename threshold_t, typename index_t, typename metadata_storage_t, typename offset_t>
33 auto constexpr get_node_alignment()
34 {
35  auto total = index_type(std::max(sizeof(threshold_t), sizeof(index_t)) +
36  sizeof(metadata_storage_t) + sizeof(offset_t));
37  auto result = index_type{8};
38  if (total > result) { result = index_type{16}; }
39  if (total > result) { result = index_type{32}; }
40  if (total > result) { result = index_type{64}; }
41  if (total > result) { result = index_type{128}; }
42  if (total > result) { result = index_type{256}; }
43  if (total > result) { result = total; }
44  return result;
45 }
46 
47 } // namespace detail
48 
49 /* @brief A single node in a forest model
50  *
51  * Note that this implementation includes NO error checking for poorly-chosen
52  * template types. If the types are not large enough to hold the required data,
53  * an incorrect node will be silently constructed. Error checking occurs
54  * instead at the time of construction of the entire forest.
55  *
56  * @tparam layout_v The layout for nodes within the forest
57  *
58  * @tparam threshold_t The type used as a threshold for evaluating a non-leaf
59  * node or (when possible) the output of a leaf node. For non-categorical
60  * nodes, if an input value is less than this threshold, the node evaluates to
61  * true. For leaf nodes, output values will only be stored as this type if it
62  * matches the leaf output type expected by the forest. Typically, this type is
63  * either float or double.
64  *
65  * @tparam index_t The type used as an index to the output data for leaf nodes,
66  * or to the categorical set for a categorical non-leaf node. This type should
67  * be large enough to index the entire array of output data or categorical sets
68  * stored in the forest. Typically, this type is either uint32_t or uint64_t.
69  * Smaller types offer no benefit, since this value is stored in a union with
70  * threshold_t, which is at least 32 bits.
71  *
72  * @tparam metadata_storage_t An unsigned integral type used for a bit-wise
73  * representation of metadata about this node. The first three bits encode
74  * whether or not this is a leaf node, whether or not we should default to the
75  * more distant child in case of missing values, and whether or not this node
76  * is categorical. The remaining bits are used to encode the feature index for
77  * this node. Thus, uint8_t may be used for 2**(8 - 3) = 32 or fewer features,
78  * uint16_t for 2**(16 - 3) = 8192 or fewer, and uint32_t for 536870912 or
79  * fewer features.
80  *
81  * @tparam offset_t An integral type used to indicate the offset from
82  * this node to its most distant child. This type must be large enough to store
83  * the largest such offset in the forest model.
84  */
85 template <tree_layout layout_v,
86  typename threshold_t,
87  typename index_t,
88  typename metadata_storage_t,
89  typename offset_t>
90 struct alignas(
92  detail::get_node_alignment<threshold_t, index_t, metadata_storage_t, offset_t>()) node {
93  // @brief An alias for layout_v
94  auto constexpr static const layout = layout_v;
95  // @brief An alias for threshold_t
96  using threshold_type = threshold_t;
97  // @brief An alias for index_t
98  using index_type = index_t;
99  /* @brief A union to hold either a threshold value or index
100  *
101  * All nodes will need EITHER a threshold value, an output value, OR an index
102  * to data elsewhere that wil be used either for evaluating the node (e.g. an
103  * index to a categorical set) or creating an output (e.g. an index to vector
104  * leaf output). This union allows us to store either of these values without
105  * using additional space for the unused value.
106  */
107  union value_type {
108  threshold_t value;
109  index_t index;
110  };
112  using metadata_storage_type = metadata_storage_t;
114  using offset_type = offset_t;
115 
116  // TODO(wphicks): Add custom type to ensure given child offset is at least
117  // one
118 #pragma GCC diagnostic push
119 #pragma GCC diagnostic ignored "-Wnarrowing"
120  HOST DEVICE constexpr node(threshold_type value = threshold_type{},
121  bool is_leaf_node = true,
122  bool default_to_distant_child = false,
123  bool is_categorical_node = false,
125  offset_type distant_child_offset = offset_type{})
126  : aligned_data{
127  .inner_data = {
128  {.value = value},
129  distant_child_offset,
130  construct_metadata(is_leaf_node, default_to_distant_child, is_categorical_node, feature)}}
131  {
132  }
133 
134  HOST DEVICE constexpr node(index_type index,
135  bool is_leaf_node = true,
136  bool default_to_distant_child = false,
137  bool is_categorical_node = false,
139  offset_type distant_child_offset = offset_type{})
140  : aligned_data{
141  .inner_data = {
142  {.index = index},
143  distant_child_offset,
144  construct_metadata(is_leaf_node, default_to_distant_child, is_categorical_node, feature)}}
145  {
146  }
147 #pragma GCC diagnostic pop
148 
149  /* The index of the feature for this node */
150  HOST DEVICE auto constexpr feature_index() const
151  {
152  return aligned_data.inner_data.metadata & FEATURE_MASK;
153  }
154  /* Whether or not this node is a leaf node */
155  HOST DEVICE auto constexpr is_leaf() const
156  {
157  return !bool(aligned_data.inner_data.distant_offset);
158  }
159  /* Whether or not to default to distant child in case of missing values */
160  HOST DEVICE auto constexpr default_distant() const
161  {
162  return bool(aligned_data.inner_data.metadata & DEFAULT_DISTANT_MASK);
163  }
164  /* Whether or not this node is a categorical node */
165  HOST DEVICE auto constexpr is_categorical() const
166  {
167  return bool(aligned_data.inner_data.metadata & CATEGORICAL_MASK);
168  }
169  /* The offset to the child of this node if it evaluates to given condition */
170  HOST DEVICE auto constexpr child_offset(bool condition) const
171  {
172  if constexpr (layout == tree_layout::depth_first) {
173  return offset_type{1} + condition * (aligned_data.inner_data.distant_offset - offset_type{1});
174  } else if constexpr (layout == tree_layout::breadth_first) {
175  return condition * offset_type{1} + (aligned_data.inner_data.distant_offset - offset_type{1});
176  } else {
177  static_assert(layout == tree_layout::depth_first);
178  }
179  }
180  /* The threshold value for this node */
181  HOST DEVICE auto constexpr threshold() const
182  {
183  return aligned_data.inner_data.stored_value.value;
184  }
185 
186  /* The index value for this node */
187  HOST DEVICE auto const& index() const { return aligned_data.inner_data.stored_value.index; }
188  /* The output value for this node
189  *
190  * @tparam output_t The expected output type for this node.
191  */
192  template <bool has_vector_leaves>
193  HOST DEVICE auto constexpr output() const
194  {
195  if constexpr (has_vector_leaves) {
196  return aligned_data.inner_data.stored_value.index;
197  } else {
198  return aligned_data.inner_data.stored_value.value;
199  }
200  }
201 
202  private:
203  /* Define all bit masks required to extract information from the stored
204  * metadata. The first bit tells us whether or not this is a leaf node, the
205  * second tells us whether or not we should default to the distant child in
206  * the case of a missing value, and the third tells us whether or not this is
207  * a categorical node. The remaining bits indicate the index of the feature
208  * for this node */
209  auto constexpr static const LEAF_BIT =
211  auto constexpr static const LEAF_MASK = metadata_storage_type(1 << LEAF_BIT);
212  auto constexpr static const DEFAULT_DISTANT_BIT = metadata_storage_type(LEAF_BIT - 1);
213  auto constexpr static const DEFAULT_DISTANT_MASK =
214  metadata_storage_type(1 << DEFAULT_DISTANT_BIT);
215  auto constexpr static const CATEGORICAL_BIT = metadata_storage_type(DEFAULT_DISTANT_BIT - 1);
216  auto constexpr static const CATEGORICAL_MASK = metadata_storage_type(1 << CATEGORICAL_BIT);
217  auto constexpr static const FEATURE_MASK =
218  metadata_storage_type(~(LEAF_MASK | DEFAULT_DISTANT_MASK | CATEGORICAL_MASK));
219 
220  // Helper function for bit packing with the above masks
221  auto static constexpr construct_metadata(bool is_leaf_node = true,
222  bool default_to_distant_child = false,
223  bool is_categorical_node = false,
225  {
226  return metadata_storage_type(
227  (is_leaf_node << LEAF_BIT) + (default_to_distant_child << DEFAULT_DISTANT_BIT) +
228  (is_categorical_node << CATEGORICAL_BIT) + (feature & FEATURE_MASK));
229  }
230 
231  auto static constexpr const byte_size =
232  detail::get_node_alignment<threshold_t, index_t, metadata_storage_t, offset_t>();
233 
234  struct inner_data_type {
235  value_type stored_value;
236  // TODO (wphicks): It may be possible to store both of the following together
237  // to save bytes
238  offset_type distant_offset;
239  metadata_storage_type metadata;
240  };
241  union aligned_data_type {
242  inner_data_type inner_data;
243  char spacer_data[byte_size];
244  };
245 
246  aligned_data_type aligned_data;
247 };
248 
249 } // namespace fil
250 } // namespace experimental
} // namespace ML
#define DEVICE
Definition: gpu_support.hpp:34
#define HOST
Definition: gpu_support.hpp:33
math_t max(math_t a, math_t b)
Definition: learning_rate.h:26
constexpr auto get_node_alignment()
Definition: node.hpp:33
tree_layout
Definition: tree_layout.hpp:20
uint32_t index_type
Definition: index_type.hpp:21
Definition: dbscan.hpp:27
Definition: node.hpp:91
HOST DEVICE constexpr auto default_distant() const
Definition: node.hpp:159
HOST DEVICE constexpr auto threshold() const
Definition: node.hpp:180
HOST DEVICE constexpr auto is_categorical() const
Definition: node.hpp:164
HOST DEVICE constexpr auto child_offset(bool condition) const
Definition: node.hpp:169
HOST DEVICE constexpr auto output() const
Definition: node.hpp:192
threshold_t threshold_type
Definition: node.hpp:95
metadata_storage_t metadata_storage_type
An alias for metadata_storage_t.
Definition: node.hpp:111
HOST DEVICE constexpr auto is_leaf() const
Definition: node.hpp:154
HOST DEVICE auto const & index() const
Definition: node.hpp:186
HOST DEVICE constexpr auto feature_index() const
Definition: node.hpp:149
HOST constexpr DEVICE node(threshold_type value=threshold_type{}, bool is_leaf_node=true, bool default_to_distant_child=false, bool is_categorical_node=false, metadata_storage_type feature=metadata_storage_type{}, offset_type distant_child_offset=offset_type{})
Definition: node.hpp:119
index_t index_type
Definition: node.hpp:97
constexpr static auto const layout
Definition: node.hpp:93
offset_t offset_type
An alias for offset_t.
Definition: node.hpp:113
threshold_t value
Definition: node.hpp:107
index_t index
Definition: node.hpp:108