experimental/row_operators.cuh
1 /*
2  * Copyright (c) 2022-2025, 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 
17 #pragma once
18 
20 #include <cudf/detail/iterator.cuh>
21 #include <cudf/detail/utilities/algorithm.cuh>
22 #include <cudf/detail/utilities/assert.cuh>
23 #include <cudf/hashing/detail/default_hash.cuh>
24 #include <cudf/hashing/detail/hashing.hpp>
25 #include <cudf/lists/detail/dremel.hpp>
26 #include <cudf/lists/list_device_view.cuh>
27 #include <cudf/lists/lists_column_device_view.cuh>
28 #include <cudf/sorting.hpp>
29 #include <cudf/structs/structs_column_device_view.cuh>
30 #include <cudf/table/row_operators.cuh>
35 
36 #include <cuda/std/functional>
37 #include <cuda/std/limits>
38 #include <cuda/std/optional>
39 #include <cuda/std/tuple>
40 #include <cuda/std/utility>
41 #include <thrust/detail/use_default.h>
42 #include <thrust/equal.h>
43 #include <thrust/execution_policy.h>
44 #include <thrust/functional.h>
45 #include <thrust/iterator/counting_iterator.h>
46 #include <thrust/iterator/iterator_adaptor.h>
47 #include <thrust/iterator/iterator_categories.h>
48 #include <thrust/iterator/iterator_facade.h>
49 #include <thrust/iterator/transform_iterator.h>
50 #include <thrust/logical.h>
51 #include <thrust/swap.h>
52 #include <thrust/transform_reduce.h>
53 
54 #include <memory>
55 #include <type_traits>
56 
57 namespace CUDF_EXPORT cudf {
58 
59 namespace experimental {
60 
76 template <cudf::type_id t>
79  using type = std::conditional_t<t == type_id::STRUCT or t == type_id::LIST, void, id_to_type<t>>;
80 };
81 
82 namespace row {
83 
84 enum class lhs_index_type : size_type {};
85 enum class rhs_index_type : size_type {};
86 
100 template <typename Index, typename Underlying = std::underlying_type_t<Index>>
101 struct strong_index_iterator : public thrust::iterator_facade<strong_index_iterator<Index>,
102  Index,
103  thrust::use_default,
104  thrust::random_access_traversal_tag,
105  Index,
106  Underlying> {
107  using super_t =
108  thrust::iterator_adaptor<strong_index_iterator<Index>, Index>;
109 
115  explicit constexpr strong_index_iterator(Underlying n) : begin{n} {}
116 
117  friend class thrust::iterator_core_access;
118 
119  private:
120  __device__ constexpr void increment() { ++begin; }
121  __device__ constexpr void decrement() { --begin; }
122 
123  __device__ constexpr void advance(Underlying n) { begin += n; }
124 
125  __device__ constexpr bool equal(strong_index_iterator<Index> const& other) const noexcept
126  {
127  return begin == other.begin;
128  }
129 
130  __device__ constexpr Index dereference() const noexcept { return static_cast<Index>(begin); }
131 
132  __device__ constexpr Underlying distance_to(
133  strong_index_iterator<Index> const& other) const noexcept
134  {
135  return other.begin - begin;
136  }
137 
138  Underlying begin{};
139 };
140 
144 using lhs_iterator = strong_index_iterator<lhs_index_type>;
145 
149 using rhs_iterator = strong_index_iterator<rhs_index_type>;
150 
151 namespace lexicographic {
152 
168  template <typename Element>
169  __device__ constexpr weak_ordering operator()(Element const lhs, Element const rhs) const noexcept
170  {
171  return detail::compare_elements(lhs, rhs);
172  }
173 };
174 
188  template <typename Element, CUDF_ENABLE_IF(not std::is_floating_point_v<Element>)>
189  __device__ constexpr weak_ordering operator()(Element const lhs, Element const rhs) const noexcept
190  {
191  return detail::compare_elements(lhs, rhs);
192  }
193 
201  template <typename Element, CUDF_ENABLE_IF(std::is_floating_point_v<Element>)>
202  __device__ constexpr weak_ordering operator()(Element const lhs, Element const rhs) const noexcept
203  {
204  if (isnan(lhs)) {
205  return isnan(rhs) ? weak_ordering::EQUIVALENT : weak_ordering::GREATER;
206  } else if (isnan(rhs)) {
207  return weak_ordering::LESS;
208  }
209 
210  return detail::compare_elements(lhs, rhs);
211  }
212 };
213 
214 using optional_dremel_view = cuda::std::optional<detail::dremel_device_view const>;
215 
216 // The has_nested_columns template parameter of the device_row_comparator is
217 // necessary to help the compiler optimize our code. Without it, the list and
218 // struct view specializations are present in the code paths used for primitive
219 // types, and the compiler fails to inline this nearly as well resulting in a
220 // significant performance drop. As a result, there is some minor tension in
221 // the current design between the presence of this parameter and the way that
222 // the Dremel data is passed around, first as a
223 // std::optional<device_span<dremel_device_view>> in the
224 // preprocessed_table/device_row_comparator (which is always valid when
225 // has_nested_columns and is otherwise invalid) that is then unpacked to a
226 // cuda::std::optional<dremel_device_view> at the element_comparator level (which
227 // is always valid for a list column and otherwise invalid). We cannot use an
228 // additional template parameter for the element_comparator on a per-column
229 // basis because we cannot conditionally define dremel_device_view member
230 // variables without jumping through extra hoops with inheritance, so the
231 // cuda::std::optional<dremel_device_view> member must be an optional rather than
232 // a raw dremel_device_view.
263 template <bool has_nested_columns,
264  typename Nullate,
265  typename PhysicalElementComparator = sorting_physical_element_comparator>
267  public:
268  friend class self_comparator;
269  friend class two_table_comparator;
270 
291  Nullate check_nulls,
292  table_device_view lhs,
293  table_device_view rhs,
294  device_span<detail::dremel_device_view const> l_dremel_device_views,
295  device_span<detail::dremel_device_view const> r_dremel_device_views,
296  cuda::std::optional<device_span<int const>> depth = cuda::std::nullopt,
297  cuda::std::optional<device_span<order const>> column_order = cuda::std::nullopt,
298  cuda::std::optional<device_span<null_order const>> null_precedence = cuda::std::nullopt,
299  PhysicalElementComparator comparator = {}) noexcept
300  : _lhs{lhs},
301  _rhs{rhs},
302  _l_dremel(l_dremel_device_views),
303  _r_dremel(r_dremel_device_views),
304  _check_nulls{check_nulls},
305  _depth{depth},
306  _column_order{column_order},
307  _null_precedence{null_precedence},
308  _comparator{comparator}
309  {
310  }
311 
330  template <bool nested_disable = not has_nested_columns, CUDF_ENABLE_IF(nested_disable)>
332  Nullate check_nulls,
333  table_device_view lhs,
334  table_device_view rhs,
335  cuda::std::optional<device_span<order const>> column_order = cuda::std::nullopt,
336  cuda::std::optional<device_span<null_order const>> null_precedence = cuda::std::nullopt,
337  PhysicalElementComparator comparator = {}) noexcept
338  : _lhs{lhs},
339  _rhs{rhs},
340  _l_dremel{},
341  _r_dremel{},
342  _check_nulls{check_nulls},
343  _depth{},
344  _column_order{column_order},
345  _null_precedence{null_precedence},
346  _comparator{comparator}
347  {
348  }
349 
354  public:
371  __device__ element_comparator(Nullate check_nulls,
372  column_device_view lhs,
373  column_device_view rhs,
374  null_order null_precedence = null_order::BEFORE,
375  int depth = 0,
376  PhysicalElementComparator comparator = {},
377  optional_dremel_view l_dremel_device_view = {},
378  optional_dremel_view r_dremel_device_view = {})
379  : _lhs{lhs},
380  _rhs{rhs},
381  _check_nulls{check_nulls},
382  _null_precedence{null_precedence},
383  _depth{depth},
384  _l_dremel_device_view{l_dremel_device_view},
385  _r_dremel_device_view{r_dremel_device_view},
386  _comparator{comparator}
387  {
388  }
389 
398  template <typename Element,
399  CUDF_ENABLE_IF(cudf::is_relationally_comparable<Element, Element>())>
400  __device__ cuda::std::pair<weak_ordering, int> operator()(
401  size_type const lhs_element_index, size_type const rhs_element_index) const noexcept
402  {
403  if (_check_nulls) {
404  bool const lhs_is_null{_lhs.is_null(lhs_element_index)};
405  bool const rhs_is_null{_rhs.is_null(rhs_element_index)};
406 
407  if (lhs_is_null or rhs_is_null) { // at least one is null
408  return cuda::std::pair(null_compare(lhs_is_null, rhs_is_null, _null_precedence), _depth);
409  }
410  }
411 
412  return cuda::std::pair(_comparator(_lhs.element<Element>(lhs_element_index),
413  _rhs.element<Element>(rhs_element_index)),
414  cuda::std::numeric_limits<int>::max());
415  }
416 
424  template <typename Element,
425  CUDF_ENABLE_IF(not cudf::is_relationally_comparable<Element, Element>() and
426  (not has_nested_columns or not cudf::is_nested<Element>()))>
427  __device__ cuda::std::pair<weak_ordering, int> operator()(size_type const,
428  size_type const) const noexcept
429  {
430  CUDF_UNREACHABLE("Attempted to compare elements of uncomparable types.");
431  }
432 
441  template <typename Element,
442  CUDF_ENABLE_IF(has_nested_columns and std::is_same_v<Element, cudf::struct_view>)>
443  __device__ cuda::std::pair<weak_ordering, int> operator()(
444  size_type const lhs_element_index, size_type const rhs_element_index) const noexcept
445  {
446  column_device_view lcol = _lhs;
447  column_device_view rcol = _rhs;
448  int depth = _depth;
449  while (lcol.type().id() == type_id::STRUCT) {
450  bool const lhs_is_null{lcol.is_null(lhs_element_index)};
451  bool const rhs_is_null{rcol.is_null(rhs_element_index)};
452 
453  if (lhs_is_null or rhs_is_null) { // at least one is null
454  weak_ordering state = null_compare(lhs_is_null, rhs_is_null, _null_precedence);
455  return cuda::std::pair(state, depth);
456  }
457 
458  if (lcol.num_child_columns() == 0) {
459  return cuda::std::pair(weak_ordering::EQUIVALENT, cuda::std::numeric_limits<int>::max());
460  }
461 
462  // Non-empty structs have been modified to only have 1 child when using this.
465  ++depth;
466  }
467 
468  return cudf::type_dispatcher<dispatch_void_if_nested>(
469  lcol.type(),
470  element_comparator{_check_nulls, lcol, rcol, _null_precedence, depth, _comparator},
471  lhs_element_index,
472  rhs_element_index);
473  }
474 
483  template <typename Element,
484  CUDF_ENABLE_IF(has_nested_columns and std::is_same_v<Element, cudf::list_view>)>
485  __device__ cuda::std::pair<weak_ordering, int> operator()(size_type lhs_element_index,
486  size_type rhs_element_index)
487  {
488  // only order top-NULLs according to null_order
489  auto const is_l_row_null = _lhs.is_null(lhs_element_index);
490  auto const is_r_row_null = _rhs.is_null(rhs_element_index);
491  if (is_l_row_null || is_r_row_null) {
492  return cuda::std::pair(null_compare(is_l_row_null, is_r_row_null, _null_precedence),
493  _depth);
494  }
495 
496  // These are all the values from the Dremel encoding.
497  auto const l_max_def_level = _l_dremel_device_view->max_def_level;
498  auto const r_max_def_level = _r_dremel_device_view->max_def_level;
499  auto const l_def_levels = _l_dremel_device_view->def_levels;
500  auto const r_def_levels = _r_dremel_device_view->def_levels;
501  auto const l_rep_levels = _l_dremel_device_view->rep_levels;
502  auto const r_rep_levels = _r_dremel_device_view->rep_levels;
503 
504  // Traverse the nested list hierarchy to get a column device view
505  // pointing to the underlying child data.
506  column_device_view lcol = _lhs.slice(lhs_element_index, 1);
507  column_device_view rcol = _rhs.slice(rhs_element_index, 1);
508 
509  while (lcol.type().id() == type_id::LIST) {
512  }
513 
514  // These start and end values indicate the start and end points of all
515  // the elements of the lists in the current list element
516  // (`[lhs|rhs]_element_index`) that we are comparing.
517  auto const l_offsets = _l_dremel_device_view->offsets;
518  auto const r_offsets = _r_dremel_device_view->offsets;
519  auto l_start = l_offsets[lhs_element_index];
520  auto l_end = l_offsets[lhs_element_index + 1];
521  auto r_start = r_offsets[rhs_element_index];
522  auto r_end = r_offsets[rhs_element_index + 1];
523 
524  // This comparator will be used to compare leaf (non-nested) data types.
525  auto comparator =
526  element_comparator{_check_nulls, lcol, rcol, _null_precedence, _depth, _comparator};
527 
528  // Loop over each element in the encoding. Note that this includes nulls
529  // and empty lists, so not every index corresponds to an actual element
530  // in the child column. The element_index is used to keep track of the current
531  // child element that we're actually comparing.
532  for (int l_dremel_index = l_start, r_dremel_index = r_start, element_index = 0;
533  l_dremel_index < l_end and r_dremel_index < r_end;
534  ++l_dremel_index, ++r_dremel_index) {
535  auto const l_rep_level = l_rep_levels[l_dremel_index];
536  auto const r_rep_level = r_rep_levels[r_dremel_index];
537 
538  // early exit for smaller sub-list
539  if (l_rep_level != r_rep_level) {
540  // the lower repetition level is a smaller sub-list
541  return l_rep_level < r_rep_level ? cuda::std::pair(weak_ordering::LESS, _depth)
542  : cuda::std::pair(weak_ordering::GREATER, _depth);
543  }
544 
545  // only compare if left and right are at same nesting level
546  auto const l_def_level = l_def_levels[l_dremel_index];
547  auto const r_def_level = r_def_levels[r_dremel_index];
548 
549  // either left or right are empty or NULLs of arbitrary nesting
550  if (l_def_level < l_max_def_level || r_def_level < r_max_def_level) {
551  // in the fully unraveled version of the list column, only the
552  // most nested NULLs and leafs are present
553  // In this rare condition that we get to the most nested NULL, we increment
554  // element_index because either both rows have a deeply nested NULL at the
555  // same position, and we'll "continue" in our iteration, or we will early
556  // exit if only one of the rows has a deeply nested NULL
557  if ((lcol.nullable() and l_def_levels[l_dremel_index] == l_max_def_level - 1) or
558  (rcol.nullable() and r_def_levels[r_dremel_index] == r_max_def_level - 1)) {
559  ++element_index;
560  }
561  if (l_def_level == r_def_level) { continue; }
562  // We require [] < [NULL] < [leaf] for nested nulls.
563  // The null_precedence only affects top level nulls.
564  return l_def_level < r_def_level ? cuda::std::pair(weak_ordering::LESS, _depth)
565  : cuda::std::pair(weak_ordering::GREATER, _depth);
566  }
567 
568  // finally, compare leaf to leaf
569  weak_ordering state{weak_ordering::EQUIVALENT};
570  int last_null_depth = _depth;
571  cuda::std::tie(state, last_null_depth) = cudf::type_dispatcher<dispatch_void_if_nested>(
572  lcol.type(), comparator, element_index, element_index);
573  if (state != weak_ordering::EQUIVALENT) { return cuda::std::pair(state, _depth); }
574  ++element_index;
575  }
576 
577  // If we have reached this stage, we know that definition levels,
578  // repetition levels, and actual elements are identical in both list
579  // columns up to the `min(l_end - l_start, r_end - r_start)` element of
580  // the Dremel encoding. However, two lists can only compare equivalent if
581  // they are of the same length. Otherwise, the shorter of the two is less
582  // than the longer. This final check determines the appropriate resulting
583  // ordering by checking how many total elements each list is composed of.
584  return cuda::std::pair(detail::compare_elements(l_end - l_start, r_end - r_start), _depth);
585  }
586 
587  private:
588  column_device_view const _lhs;
589  column_device_view const _rhs;
590  Nullate const _check_nulls;
591  null_order const _null_precedence;
592  int const _depth;
593  optional_dremel_view _l_dremel_device_view;
594  optional_dremel_view _r_dremel_device_view;
595  PhysicalElementComparator const _comparator;
596  };
597 
598  public:
608  __device__ constexpr weak_ordering operator()(size_type const lhs_index,
609  size_type const rhs_index) const noexcept
610  {
611  int last_null_depth = cuda::std::numeric_limits<int>::max();
612  size_type list_column_index{-1};
613  for (size_type i = 0; i < _lhs.num_columns(); ++i) {
614  if (_lhs.column(i).type().id() == type_id::LIST) { ++list_column_index; }
615 
616  int const depth = _depth.has_value() ? (*_depth)[i] : 0;
617  if (depth > last_null_depth) { continue; }
618 
619  bool const ascending =
620  _column_order.has_value() ? (*_column_order)[i] == order::ASCENDING : true;
621 
622  null_order const null_precedence =
623  _null_precedence.has_value() ? (*_null_precedence)[i] : null_order::BEFORE;
624 
625  // TODO: At what point do we verify that the columns of lhs and rhs are
626  // all of the same types? I assume that it's already happened before
627  // here, otherwise the current code would be failing.
628  auto const [l_dremel_i, r_dremel_i] =
629  _lhs.column(i).type().id() == type_id::LIST
630  ? cuda::std::make_tuple(optional_dremel_view(_l_dremel[list_column_index]),
631  optional_dremel_view(_r_dremel[list_column_index]))
632  : cuda::std::make_tuple(optional_dremel_view{}, optional_dremel_view{});
633 
634  auto element_comp = element_comparator{_check_nulls,
635  _lhs.column(i),
636  _rhs.column(i),
637  null_precedence,
638  depth,
639  _comparator,
640  l_dremel_i,
641  r_dremel_i};
642 
643  weak_ordering state;
644  cuda::std::tie(state, last_null_depth) =
645  cudf::type_dispatcher(_lhs.column(i).type(), element_comp, lhs_index, rhs_index);
646 
647  if (state == weak_ordering::EQUIVALENT) { continue; }
648 
649  return ascending
650  ? state
651  : (state == weak_ordering::GREATER ? weak_ordering::LESS : weak_ordering::GREATER);
652  }
653  return weak_ordering::EQUIVALENT;
654  }
655 
656  private:
657  table_device_view const _lhs;
658  table_device_view const _rhs;
661  Nullate const _check_nulls;
662  cuda::std::optional<device_span<int const>> const _depth;
663  cuda::std::optional<device_span<order const>> const _column_order;
664  cuda::std::optional<device_span<null_order const>> const _null_precedence;
665  PhysicalElementComparator const _comparator;
666 }; // class device_row_comparator
667 
678 template <typename Comparator, weak_ordering... values>
679 struct weak_ordering_comparator_impl {
680  static_assert(not((weak_ordering::EQUIVALENT == values) && ...),
681  "weak_ordering_comparator should not be used for pure equality comparisons. The "
682  "`row_equality_comparator` should be used instead");
683 
684  template <typename LhsType, typename RhsType>
685  __device__ constexpr bool operator()(LhsType const lhs_index,
686  RhsType const rhs_index) const noexcept
687  {
688  weak_ordering const result = comparator(lhs_index, rhs_index);
689  return ((result == values) || ...);
690  }
691  Comparator const comparator;
692 };
693 
700 template <typename Comparator>
701 struct less_comparator : weak_ordering_comparator_impl<Comparator, weak_ordering::LESS> {
707  less_comparator(Comparator const& comparator)
708  : weak_ordering_comparator_impl<Comparator, weak_ordering::LESS>{comparator}
709  {
710  }
711 };
712 
720 template <typename Comparator>
722  : weak_ordering_comparator_impl<Comparator, weak_ordering::LESS, weak_ordering::EQUIVALENT> {
728  less_equivalent_comparator(Comparator const& comparator)
729  : weak_ordering_comparator_impl<Comparator, weak_ordering::LESS, weak_ordering::EQUIVALENT>{
730  comparator}
731  {
732  }
733 };
734 
742  std::invoke_result_t<decltype(table_device_view::create), table_view, rmm::cuda_stream_view>;
743 
766  static std::shared_ptr<preprocessed_table> create(table_view const& table,
767  host_span<order const> column_order,
768  host_span<null_order const> null_precedence,
769  rmm::cuda_stream_view stream);
770 
792  static std::pair<std::shared_ptr<preprocessed_table>, std::shared_ptr<preprocessed_table>> create(
793  table_view const& lhs,
794  table_view const& rhs,
795  host_span<order const> column_order,
796  host_span<null_order const> null_precedence,
797  rmm::cuda_stream_view stream);
798 
799  private:
800  friend class self_comparator;
801  friend class two_table_comparator;
802 
822  static std::shared_ptr<preprocessed_table> create(
823  table_view const& preprocessed_input,
824  std::vector<int>&& verticalized_col_depths,
825  std::vector<std::unique_ptr<column>>&& transformed_columns,
826  host_span<order const> column_order,
827  host_span<null_order const> null_precedence,
828  bool has_ranked_children,
829  rmm::cuda_stream_view stream);
830 
858  rmm::device_uvector<order>&& column_order,
859  rmm::device_uvector<null_order>&& null_precedence,
861  std::vector<detail::dremel_data>&& dremel_data,
863  std::vector<std::unique_ptr<column>>&& transformed_columns,
864  bool has_ranked_children);
865 
867  rmm::device_uvector<order>&& column_order,
868  rmm::device_uvector<null_order>&& null_precedence,
870  std::vector<std::unique_ptr<column>>&& transformed_columns,
871  bool has_ranked_children);
872 
878  operator table_device_view() { return *_t; }
879 
886  [[nodiscard]] cuda::std::optional<device_span<order const>> column_order() const
887  {
888  return _column_order.size() ? cuda::std::optional<device_span<order const>>(_column_order)
889  : cuda::std::nullopt;
890  }
891 
899  [[nodiscard]] cuda::std::optional<device_span<null_order const>> null_precedence() const
900  {
901  return _null_precedence.size()
902  ? cuda::std::optional<device_span<null_order const>>(_null_precedence)
903  : cuda::std::nullopt;
904  }
905 
914  [[nodiscard]] cuda::std::optional<device_span<int const>> depths() const
915  {
916  return _depths.size() ? cuda::std::optional<device_span<int const>>(_depths)
917  : cuda::std::nullopt;
918  }
919 
920  [[nodiscard]] device_span<detail::dremel_device_view const> dremel_device_views() const
921  {
922  if (_dremel_device_views.has_value()) {
923  return device_span<detail::dremel_device_view const>(*_dremel_device_views);
924  } else {
925  return {};
926  }
927  }
928 
929  template <typename PhysicalElementComparator>
930  void check_physical_element_comparator()
931  {
932  if constexpr (!std::is_same_v<PhysicalElementComparator, sorting_physical_element_comparator>) {
933  CUDF_EXPECTS(!_has_ranked_children,
934  "The input table has nested type children and they were transformed using a "
935  "different type of physical element comparator.");
936  }
937  }
938 
939  private:
940  table_device_view_owner const _t;
941  rmm::device_uvector<order> const _column_order;
942  rmm::device_uvector<null_order> const _null_precedence;
943  rmm::device_uvector<size_type> const _depths;
944 
945  // Dremel encoding of list columns used for the comparison algorithm
946  cuda::std::optional<std::vector<detail::dremel_data>> _dremel_data;
947  cuda::std::optional<rmm::device_uvector<detail::dremel_device_view>> _dremel_device_views;
948 
949  // Intermediate columns generated from transforming nested children columns into
950  // integers columns using `cudf::rank()`, need to be kept alive.
951  std::vector<std::unique_ptr<column>> _transformed_columns;
952 
953  // Flag to record if the input table was preprocessed to transform any nested children column(s)
954  // into integer column(s) using `cudf::rank`.
955  bool const _has_ranked_children;
956 };
957 
972  public:
988  host_span<order const> column_order = {},
989  host_span<null_order const> null_precedence = {},
991  : d_t{preprocessed_table::create(t, column_order, null_precedence, stream)}
992  {
993  }
994 
1004  self_comparator(std::shared_ptr<preprocessed_table> t) : d_t{std::move(t)} {}
1005 
1034  template <bool has_nested_columns,
1035  typename Nullate,
1036  typename PhysicalElementComparator = sorting_physical_element_comparator>
1037  auto less(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const
1038  {
1039  d_t->check_physical_element_comparator<PhysicalElementComparator>();
1040 
1041  return less_comparator{
1042  device_row_comparator<has_nested_columns, Nullate, PhysicalElementComparator>{
1043  nullate,
1044  *d_t,
1045  *d_t,
1046  d_t->dremel_device_views(),
1047  d_t->dremel_device_views(),
1048  d_t->depths(),
1049  d_t->column_order(),
1050  d_t->null_precedence(),
1051  comparator}};
1052  }
1053 
1055  template <bool has_nested_columns,
1056  typename Nullate,
1057  typename PhysicalElementComparator = sorting_physical_element_comparator>
1058  auto less_equivalent(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const
1059  {
1060  d_t->check_physical_element_comparator<PhysicalElementComparator>();
1061 
1062  return less_equivalent_comparator{
1063  device_row_comparator<has_nested_columns, Nullate, PhysicalElementComparator>{
1064  nullate,
1065  *d_t,
1066  *d_t,
1067  d_t->dremel_device_views(),
1068  d_t->dremel_device_views(),
1069  d_t->depths(),
1070  d_t->column_order(),
1071  d_t->null_precedence(),
1072  comparator}};
1073  }
1074 
1075  private:
1076  std::shared_ptr<preprocessed_table> d_t;
1077 };
1078 
1079 // @cond
1080 template <typename Comparator>
1081 struct strong_index_comparator_adapter {
1082  strong_index_comparator_adapter(Comparator const& comparator) : comparator{comparator} {}
1083 
1084  __device__ constexpr weak_ordering operator()(lhs_index_type const lhs_index,
1085  rhs_index_type const rhs_index) const noexcept
1086  {
1087  return comparator(static_cast<cudf::size_type>(lhs_index),
1088  static_cast<cudf::size_type>(rhs_index));
1089  }
1090 
1091  __device__ constexpr weak_ordering operator()(rhs_index_type const rhs_index,
1092  lhs_index_type const lhs_index) const noexcept
1093  {
1094  auto const left_right_ordering =
1095  comparator(static_cast<cudf::size_type>(lhs_index), static_cast<cudf::size_type>(rhs_index));
1096 
1097  // Invert less/greater values to reflect right to left ordering
1098  if (left_right_ordering == weak_ordering::LESS) {
1099  return weak_ordering::GREATER;
1100  } else if (left_right_ordering == weak_ordering::GREATER) {
1101  return weak_ordering::LESS;
1102  }
1103  return weak_ordering::EQUIVALENT;
1104  }
1105 
1106  Comparator const comparator;
1107 };
1108 // @endcond
1109 
1125  public:
1145  table_view const& right,
1146  host_span<order const> column_order = {},
1147  host_span<null_order const> null_precedence = {},
1149 
1164  two_table_comparator(std::shared_ptr<preprocessed_table> left,
1165  std::shared_ptr<preprocessed_table> right)
1166  : d_left_table{std::move(left)}, d_right_table{std::move(right)}
1167  {
1168  }
1169 
1206  template <bool has_nested_columns,
1207  typename Nullate,
1208  typename PhysicalElementComparator = sorting_physical_element_comparator>
1209  auto less(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const
1210  {
1211  d_left_table->check_physical_element_comparator<PhysicalElementComparator>();
1212  d_right_table->check_physical_element_comparator<PhysicalElementComparator>();
1213 
1214  return less_comparator{strong_index_comparator_adapter{
1215  device_row_comparator<has_nested_columns, Nullate, PhysicalElementComparator>{
1216  nullate,
1217  *d_left_table,
1218  *d_right_table,
1219  d_left_table->dremel_device_views(),
1220  d_right_table->dremel_device_views(),
1221  d_left_table->depths(),
1222  d_left_table->column_order(),
1223  d_left_table->null_precedence(),
1224  comparator}}};
1225  }
1226 
1228  template <bool has_nested_columns,
1229  typename Nullate,
1230  typename PhysicalElementComparator = sorting_physical_element_comparator>
1231  auto less_equivalent(Nullate nullate = {}, PhysicalElementComparator comparator = {}) const
1232  {
1233  d_left_table->check_physical_element_comparator<PhysicalElementComparator>();
1234  d_right_table->check_physical_element_comparator<PhysicalElementComparator>();
1235 
1236  return less_equivalent_comparator{strong_index_comparator_adapter{
1237  device_row_comparator<has_nested_columns, Nullate, PhysicalElementComparator>{
1238  nullate,
1239  *d_left_table,
1240  *d_right_table,
1241  d_left_table->dremel_device_views(),
1242  d_right_table->dremel_device_views(),
1243  d_left_table->depths(),
1244  d_left_table->column_order(),
1245  d_left_table->null_precedence(),
1246  comparator}}};
1247  }
1248 
1249  private:
1250  std::shared_ptr<preprocessed_table> d_left_table;
1251  std::shared_ptr<preprocessed_table> d_right_table;
1252 };
1253 
1254 } // namespace lexicographic
1255 
1256 namespace hash {
1257 class row_hasher;
1258 } // namespace hash
1259 
1260 namespace equality {
1261 
1277  template <typename Element>
1278  __device__ constexpr bool operator()(Element const lhs, Element const rhs) const noexcept
1279  {
1280  return lhs == rhs;
1281  }
1282 };
1283 
1296  template <typename Element, CUDF_ENABLE_IF(not std::is_floating_point_v<Element>)>
1297  __device__ constexpr bool operator()(Element const lhs, Element const rhs) const noexcept
1298  {
1299  return lhs == rhs;
1300  }
1301 
1311  template <typename Element, CUDF_ENABLE_IF(std::is_floating_point_v<Element>)>
1312  __device__ constexpr bool operator()(Element const lhs, Element const rhs) const noexcept
1313  {
1314  return isnan(lhs) and isnan(rhs) ? true : lhs == rhs;
1315  }
1316 };
1317 
1339 template <bool has_nested_columns,
1340  typename Nullate,
1341  typename PhysicalEqualityComparator = nan_equal_physical_equality_comparator>
1343  friend class self_comparator;
1344  friend class two_table_comparator;
1345 
1346  public:
1355  __device__ constexpr bool operator()(size_type const lhs_index,
1356  size_type const rhs_index) const noexcept
1357  {
1358  auto equal_elements = [lhs_index, rhs_index, this](column_device_view l, column_device_view r) {
1359  return cudf::type_dispatcher(
1360  l.type(),
1361  element_comparator{check_nulls, l, r, nulls_are_equal, comparator},
1362  lhs_index,
1363  rhs_index);
1364  };
1365 
1366  return thrust::equal(thrust::seq, lhs.begin(), lhs.end(), rhs.begin(), equal_elements);
1367  }
1368 
1369  private:
1380  device_row_comparator(Nullate check_nulls,
1381  table_device_view lhs,
1382  table_device_view rhs,
1383  null_equality nulls_are_equal = null_equality::EQUAL,
1384  PhysicalEqualityComparator comparator = {}) noexcept
1385  : lhs{lhs},
1386  rhs{rhs},
1387  check_nulls{check_nulls},
1388  nulls_are_equal{nulls_are_equal},
1389  comparator{comparator}
1390  {
1391  }
1392 
1396  class element_comparator {
1397  public:
1410  __device__ element_comparator(Nullate check_nulls,
1411  column_device_view lhs,
1412  column_device_view rhs,
1413  null_equality nulls_are_equal = null_equality::EQUAL,
1414  PhysicalEqualityComparator comparator = {}) noexcept
1415  : lhs{lhs},
1416  rhs{rhs},
1417  check_nulls{check_nulls},
1418  nulls_are_equal{nulls_are_equal},
1419  comparator{comparator}
1420  {
1421  }
1422 
1431  template <typename Element, CUDF_ENABLE_IF(cudf::is_equality_comparable<Element, Element>())>
1432  __device__ bool operator()(size_type const lhs_element_index,
1433  size_type const rhs_element_index) const noexcept
1434  {
1435  if (check_nulls) {
1436  bool const lhs_is_null{lhs.is_null(lhs_element_index)};
1437  bool const rhs_is_null{rhs.is_null(rhs_element_index)};
1438  if (lhs_is_null and rhs_is_null) {
1439  return nulls_are_equal == null_equality::EQUAL;
1440  } else if (lhs_is_null != rhs_is_null) {
1441  return false;
1442  }
1443  }
1444 
1445  return comparator(lhs.element<Element>(lhs_element_index),
1446  rhs.element<Element>(rhs_element_index));
1447  }
1448 
1449  template <typename Element,
1450  CUDF_ENABLE_IF(not cudf::is_equality_comparable<Element, Element>() and
1451  (not has_nested_columns or not cudf::is_nested<Element>())),
1452  typename... Args>
1453  __device__ bool operator()(Args...)
1454  {
1455  CUDF_UNREACHABLE("Attempted to compare elements of uncomparable types.");
1456  }
1457 
1458  template <typename Element, CUDF_ENABLE_IF(has_nested_columns and cudf::is_nested<Element>())>
1459  __device__ bool operator()(size_type const lhs_element_index,
1460  size_type const rhs_element_index) const noexcept
1461  {
1462  column_device_view lcol = lhs.slice(lhs_element_index, 1);
1463  column_device_view rcol = rhs.slice(rhs_element_index, 1);
1464  while (lcol.type().id() == type_id::STRUCT || lcol.type().id() == type_id::LIST) {
1465  if (check_nulls) {
1466  auto lvalid = detail::make_validity_iterator<true>(lcol);
1467  auto rvalid = detail::make_validity_iterator<true>(rcol);
1468  if (nulls_are_equal == null_equality::UNEQUAL) {
1469  if (thrust::any_of(
1470  thrust::seq, lvalid, lvalid + lcol.size(), cuda::std::logical_not<bool>()) or
1471  thrust::any_of(
1472  thrust::seq, rvalid, rvalid + rcol.size(), cuda::std::logical_not<bool>())) {
1473  return false;
1474  }
1475  } else {
1476  if (not thrust::equal(thrust::seq, lvalid, lvalid + lcol.size(), rvalid)) {
1477  return false;
1478  }
1479  }
1480  }
1481  if (lcol.type().id() == type_id::STRUCT) {
1482  if (lcol.num_child_columns() == 0) { return true; }
1483  // Non-empty structs are assumed to be decomposed and contain only one child
1484  lcol = detail::structs_column_device_view(lcol).get_sliced_child(0);
1485  rcol = detail::structs_column_device_view(rcol).get_sliced_child(0);
1486  } else if (lcol.type().id() == type_id::LIST) {
1487  auto l_list_col = detail::lists_column_device_view(lcol);
1488  auto r_list_col = detail::lists_column_device_view(rcol);
1489 
1490  auto lsizes = make_list_size_iterator(l_list_col);
1491  auto rsizes = make_list_size_iterator(r_list_col);
1492  if (not thrust::equal(thrust::seq, lsizes, lsizes + lcol.size(), rsizes)) {
1493  return false;
1494  }
1495 
1496  lcol = l_list_col.get_sliced_child();
1497  rcol = r_list_col.get_sliced_child();
1498  if (lcol.size() != rcol.size()) { return false; }
1499  }
1500  }
1501 
1502  auto comp = column_comparator{
1503  element_comparator{check_nulls, lcol, rcol, nulls_are_equal, comparator}, lcol.size()};
1504  return type_dispatcher<dispatch_void_if_nested>(lcol.type(), comp);
1505  }
1506 
1507  private:
1515  struct column_comparator {
1516  element_comparator const comp;
1517  size_type const size;
1518 
1524  template <typename Element, CUDF_ENABLE_IF(cudf::is_equality_comparable<Element, Element>())>
1525  __device__ bool operator()() const noexcept
1526  {
1527  return thrust::all_of(thrust::seq,
1528  thrust::make_counting_iterator(0),
1529  thrust::make_counting_iterator(0) + size,
1530  [this](auto i) { return comp.template operator()<Element>(i, i); });
1531  }
1532 
1533  template <typename Element,
1534  CUDF_ENABLE_IF(not cudf::is_equality_comparable<Element, Element>()),
1535  typename... Args>
1536  __device__ bool operator()(Args...) const noexcept
1537  {
1538  CUDF_UNREACHABLE("Attempted to compare elements of uncomparable types.");
1539  }
1540  };
1541 
1542  column_device_view const lhs;
1543  column_device_view const rhs;
1544  Nullate const check_nulls;
1545  null_equality const nulls_are_equal;
1546  PhysicalEqualityComparator const comparator;
1547  };
1548 
1549  table_device_view const lhs;
1550  table_device_view const rhs;
1551  Nullate const check_nulls;
1552  null_equality const nulls_are_equal;
1553  PhysicalEqualityComparator const comparator;
1554 };
1555 
1573  static std::shared_ptr<preprocessed_table> create(table_view const& table,
1574  rmm::cuda_stream_view stream);
1575 
1576  private:
1577  friend class self_comparator;
1578  friend class two_table_comparator;
1579  friend class hash::row_hasher;
1580 
1581  using table_device_view_owner =
1582  std::invoke_result_t<decltype(table_device_view::create), table_view, rmm::cuda_stream_view>;
1583 
1584  preprocessed_table(table_device_view_owner&& table,
1585  std::vector<rmm::device_buffer>&& null_buffers,
1586  std::vector<std::unique_ptr<column>>&& tmp_columns)
1587  : _t(std::move(table)),
1588  _null_buffers(std::move(null_buffers)),
1589  _tmp_columns(std::move(tmp_columns))
1590  {
1591  }
1592 
1598  operator table_device_view() { return *_t; }
1599 
1600  table_device_view_owner _t;
1601  std::vector<rmm::device_buffer> _null_buffers;
1602  std::vector<std::unique_ptr<column>> _tmp_columns;
1603 };
1604 
1610  public:
1620  : d_t(preprocessed_table::create(t, stream))
1621  {
1622  }
1623 
1633  self_comparator(std::shared_ptr<preprocessed_table> t) : d_t{std::move(t)} {}
1634 
1660  template <bool has_nested_columns,
1661  typename Nullate,
1662  typename PhysicalEqualityComparator = nan_equal_physical_equality_comparator>
1663  auto equal_to(Nullate nullate = {},
1664  null_equality nulls_are_equal = null_equality::EQUAL,
1665  PhysicalEqualityComparator comparator = {}) const noexcept
1666  {
1667  return device_row_comparator<has_nested_columns, Nullate, PhysicalEqualityComparator>{
1668  nullate, *d_t, *d_t, nulls_are_equal, comparator};
1669  }
1670 
1671  private:
1672  std::shared_ptr<preprocessed_table> d_t;
1673 };
1674 
1675 // @cond
1676 template <typename Comparator>
1677 struct strong_index_comparator_adapter {
1678  strong_index_comparator_adapter(Comparator const& comparator) : comparator{comparator} {}
1679 
1680  __device__ constexpr bool operator()(lhs_index_type const lhs_index,
1681  rhs_index_type const rhs_index) const noexcept
1682  {
1683  return comparator(static_cast<cudf::size_type>(lhs_index),
1684  static_cast<cudf::size_type>(rhs_index));
1685  }
1686 
1687  __device__ constexpr bool operator()(rhs_index_type const rhs_index,
1688  lhs_index_type const lhs_index) const noexcept
1689  {
1690  return this->operator()(lhs_index, rhs_index);
1691  }
1692 
1693  Comparator const comparator;
1694 };
1695 // @endcond
1696 
1711  public:
1725  table_view const& right,
1726  rmm::cuda_stream_view stream);
1727 
1738  two_table_comparator(std::shared_ptr<preprocessed_table> left,
1739  std::shared_ptr<preprocessed_table> right)
1740  : d_left_table{std::move(left)}, d_right_table{std::move(right)}
1741  {
1742  }
1743 
1774  template <bool has_nested_columns,
1775  typename Nullate,
1776  typename PhysicalEqualityComparator = nan_equal_physical_equality_comparator>
1777  auto equal_to(Nullate nullate = {},
1778  null_equality nulls_are_equal = null_equality::EQUAL,
1779  PhysicalEqualityComparator comparator = {}) const noexcept
1780  {
1781  return strong_index_comparator_adapter{
1782  device_row_comparator<has_nested_columns, Nullate, PhysicalEqualityComparator>(
1783  nullate, *d_left_table, *d_right_table, nulls_are_equal, comparator)};
1784  }
1785 
1786  private:
1787  std::shared_ptr<preprocessed_table> d_left_table;
1788  std::shared_ptr<preprocessed_table> d_right_table;
1789 };
1790 
1791 } // namespace equality
1792 
1793 namespace hash {
1794 
1801 template <template <typename> class hash_function, typename Nullate>
1803  public:
1811  __device__ element_hasher(
1812  Nullate nulls,
1813  uint32_t seed = DEFAULT_HASH_SEED,
1814  hash_value_type null_hash = cuda::std::numeric_limits<hash_value_type>::max()) noexcept
1815  : _check_nulls(nulls), _seed(seed), _null_hash(null_hash)
1816  {
1817  }
1818 
1827  template <typename T, CUDF_ENABLE_IF(column_device_view::has_element_accessor<T>())>
1828  __device__ hash_value_type operator()(column_device_view const& col,
1829  size_type row_index) const noexcept
1830  {
1831  if (_check_nulls && col.is_null(row_index)) { return _null_hash; }
1832  return hash_function<T>{_seed}(col.element<T>(row_index));
1833  }
1834 
1843  template <typename T, CUDF_ENABLE_IF(not column_device_view::has_element_accessor<T>())>
1844  __device__ hash_value_type operator()(column_device_view const& col,
1845  size_type row_index) const noexcept
1846  {
1847  CUDF_UNREACHABLE("Unsupported type in hash.");
1848  }
1849 
1850  Nullate _check_nulls;
1851  uint32_t _seed;
1853 };
1854 
1861 template <template <typename> class hash_function, typename Nullate>
1863  friend class row_hasher;
1864 
1865  public:
1872  __device__ auto operator()(size_type row_index) const noexcept
1873  {
1874  auto it =
1875  thrust::make_transform_iterator(_table.begin(), [row_index, this](auto const& column) {
1876  return cudf::type_dispatcher<dispatch_storage_type>(
1877  column.type(),
1878  element_hasher_adapter<hash_function>{_check_nulls, _seed},
1879  column,
1880  row_index);
1881  });
1882 
1883  // Hash each element and combine all the hash values together
1884  return detail::accumulate(it, it + _table.num_columns(), _seed, [](auto hash, auto h) {
1885  return cudf::hashing::detail::hash_combine(hash, h);
1886  });
1887  }
1888 
1889  private:
1897  template <template <typename> class hash_fn>
1898  class element_hasher_adapter {
1899  static constexpr hash_value_type NULL_HASH = cuda::std::numeric_limits<hash_value_type>::max();
1900  static constexpr hash_value_type NON_NULL_HASH = 0;
1901 
1902  public:
1903  __device__ element_hasher_adapter(Nullate check_nulls, uint32_t seed) noexcept
1904  : _element_hasher(check_nulls, seed), _check_nulls(check_nulls)
1905  {
1906  }
1907 
1908  template <typename T, CUDF_ENABLE_IF(not cudf::is_nested<T>())>
1909  __device__ hash_value_type operator()(column_device_view const& col,
1910  size_type row_index) const noexcept
1911  {
1912  return _element_hasher.template operator()<T>(col, row_index);
1913  }
1914 
1915  template <typename T, CUDF_ENABLE_IF(cudf::is_nested<T>())>
1916  __device__ hash_value_type operator()(column_device_view const& col,
1917  size_type row_index) const noexcept
1918  {
1919  auto hash = hash_value_type{0};
1920  column_device_view curr_col = col.slice(row_index, 1);
1921  while (curr_col.type().id() == type_id::STRUCT || curr_col.type().id() == type_id::LIST) {
1922  if (_check_nulls) {
1923  auto validity_it = detail::make_validity_iterator<true>(curr_col);
1924  hash = detail::accumulate(
1925  validity_it, validity_it + curr_col.size(), hash, [](auto hash, auto is_valid) {
1926  return cudf::hashing::detail::hash_combine(hash,
1927  is_valid ? NON_NULL_HASH : NULL_HASH);
1928  });
1929  }
1930  if (curr_col.type().id() == type_id::STRUCT) {
1931  if (curr_col.num_child_columns() == 0) { return hash; }
1932  // Non-empty structs are assumed to be decomposed and contain only one child
1933  curr_col = detail::structs_column_device_view(curr_col).get_sliced_child(0);
1934  } else if (curr_col.type().id() == type_id::LIST) {
1935  auto list_col = detail::lists_column_device_view(curr_col);
1936  auto list_sizes = make_list_size_iterator(list_col);
1937  hash = detail::accumulate(
1938  list_sizes, list_sizes + list_col.size(), hash, [](auto hash, auto size) {
1939  return cudf::hashing::detail::hash_combine(hash, hash_fn<size_type>{}(size));
1940  });
1941  curr_col = list_col.get_sliced_child();
1942  }
1943  }
1944  for (int i = 0; i < curr_col.size(); ++i) {
1945  hash = cudf::hashing::detail::hash_combine(
1946  hash,
1947  type_dispatcher<dispatch_void_if_nested>(curr_col.type(), _element_hasher, curr_col, i));
1948  }
1949  return hash;
1950  }
1951 
1952  element_hasher<hash_fn, Nullate> const _element_hasher;
1953  Nullate const _check_nulls;
1954  };
1955 
1956  CUDF_HOST_DEVICE device_row_hasher(Nullate check_nulls,
1957  table_device_view t,
1958  uint32_t seed = DEFAULT_HASH_SEED) noexcept
1959  : _check_nulls{check_nulls}, _table{t}, _seed(seed)
1960  {
1961  }
1962 
1963  Nullate const _check_nulls;
1964  table_device_view const _table;
1965  uint32_t const _seed;
1966 };
1967 
1968 // Inject row::equality::preprocessed_table into the row::hash namespace
1969 // As a result, row::equality::preprocessed_table and row::hash::preprocessed table are the same
1970 // type and are interchangeable.
1971 using preprocessed_table = row::equality::preprocessed_table;
1972 
1977 class row_hasher {
1978  public:
1987  : d_t(preprocessed_table::create(t, stream))
1988  {
1989  }
1990 
2000  row_hasher(std::shared_ptr<preprocessed_table> t) : d_t{std::move(t)} {}
2001 
2014  template <template <typename> class hash_function = cudf::hashing::detail::default_hash,
2015  template <template <typename> class, typename>
2016  class DeviceRowHasher = device_row_hasher,
2017  typename Nullate>
2018  DeviceRowHasher<hash_function, Nullate> device_hasher(Nullate nullate = {},
2019  uint32_t seed = DEFAULT_HASH_SEED) const
2020  {
2021  return DeviceRowHasher<hash_function, Nullate>(nullate, *d_t, seed);
2022  }
2023 
2024  private:
2025  std::shared_ptr<preprocessed_table> d_t;
2026 };
2027 
2028 } // namespace hash
2029 
2030 } // namespace row
2031 
2032 } // namespace experimental
2033 } // namespace CUDF_EXPORT cudf
An immutable, non-owning view of device data as a column of elements that is trivially copyable and u...
T element(size_type element_index) const noexcept
Returns reference to element at the specified index.
CUDF_HOST_DEVICE column_device_view slice(size_type offset, size_type size) const noexcept
Get a new column_device_view which is a slice of this column.
CUDF_HOST_DEVICE size_type num_child_columns() const noexcept
Returns the number of child columns.
const_iterator< T > begin() const
Return an iterator to the first element of the column.
const_iterator< T > end() const
Returns an iterator to the element following the last element of the column.
A container of nullable device data as a column of elements.
Definition: column.hpp:47
constexpr CUDF_HOST_DEVICE type_id id() const noexcept
Returns the type identifier.
Definition: types.hpp:287
CUDF_HOST_DEVICE data_type type() const noexcept
Returns the element type.
bool is_null(size_type element_index) const noexcept
Returns whether the specified element is null.
CUDF_HOST_DEVICE bool nullable() const noexcept
Indicates whether the column can contain null elements, i.e., if it has an allocated bitmask.
Given a column_device_view, an instance of this class provides a wrapper on this compound column for ...
column_device_view get_sliced_child() const
Fetches the child column of the underlying list column with offset and size applied.
Given a column_device_view, an instance of this class provides a wrapper on this compound column for ...
column_device_view get_sliced_child(size_type idx) const
Fetches the child column of the underlying struct column.
constexpr bool operator()(size_type const lhs_index, size_type const rhs_index) const noexcept
Checks whether the row at lhs_index in the lhs table is equal to the row at rhs_index in the rhs tabl...
Comparator for performing equality comparisons between two rows of the same table.
self_comparator(std::shared_ptr< preprocessed_table > t)
Construct an owning object for performing equality comparisons between two rows of the same table.
self_comparator(table_view const &t, rmm::cuda_stream_view stream)
Construct an owning object for performing equality comparisons between two rows of the same table.
auto equal_to(Nullate nullate={}, null_equality nulls_are_equal=null_equality::EQUAL, PhysicalEqualityComparator comparator={}) const noexcept
Get the comparison operator to use on the device.
An owning object that can be used to equality compare rows of two different tables.
two_table_comparator(std::shared_ptr< preprocessed_table > left, std::shared_ptr< preprocessed_table > right)
Construct an owning object for performing equality comparisons between two rows from two tables.
auto equal_to(Nullate nullate={}, null_equality nulls_are_equal=null_equality::EQUAL, PhysicalEqualityComparator comparator={}) const noexcept
Return the binary operator for comparing rows in the table.
two_table_comparator(table_view const &left, table_view const &right, rmm::cuda_stream_view stream)
Construct an owning object for performing equality comparisons between two rows from two tables.
Computes the hash value of a row in the given table.
auto operator()(size_type row_index) const noexcept
Return the hash value of a row in the given table.
Computes the hash value of an element in the given column.
element_hasher(Nullate nulls, uint32_t seed=DEFAULT_HASH_SEED, hash_value_type null_hash=cuda::std::numeric_limits< hash_value_type >::max()) noexcept
Constructs an element_hasher object.
hash_value_type _null_hash
Hash value to use for null elements.
Computes the hash value of a row in the given table.
DeviceRowHasher< hash_function, Nullate > device_hasher(Nullate nullate={}, uint32_t seed=DEFAULT_HASH_SEED) const
Get the hash operator to use on the device.
row_hasher(table_view const &t, rmm::cuda_stream_view stream)
Construct an owning object for hashing the rows of a table.
row_hasher(std::shared_ptr< preprocessed_table > t)
Construct an owning object for hashing the rows of a table from an existing preprocessed_table.
Performs a relational comparison between two elements in two columns.
cuda::std::pair< weak_ordering, int > operator()(size_type lhs_element_index, size_type rhs_element_index)
Compares two list-type columns.
cuda::std::pair< weak_ordering, int > operator()(size_type const lhs_element_index, size_type const rhs_element_index) const noexcept
Performs a relational comparison between the specified elements.
cuda::std::pair< weak_ordering, int > operator()(size_type const, size_type const) const noexcept
Throws run-time error when columns types cannot be compared or if this class is instantiated with has...
element_comparator(Nullate check_nulls, column_device_view lhs, column_device_view rhs, null_order null_precedence=null_order::BEFORE, int depth=0, PhysicalElementComparator comparator={}, optional_dremel_view l_dremel_device_view={}, optional_dremel_view r_dremel_device_view={})
Construct type-dispatched function object for performing a relational comparison between two elements...
Computes the lexicographic comparison between 2 rows.
constexpr weak_ordering operator()(size_type const lhs_index, size_type const rhs_index) const noexcept
Checks whether the row at lhs_index in the lhs table compares lexicographically less,...
device_row_comparator(Nullate check_nulls, table_device_view lhs, table_device_view rhs, cuda::std::optional< device_span< order const >> column_order=cuda::std::nullopt, cuda::std::optional< device_span< null_order const >> null_precedence=cuda::std::nullopt, PhysicalElementComparator comparator={}) noexcept
Construct a function object for performing a lexicographic comparison between the rows of two tables....
device_row_comparator(Nullate check_nulls, table_device_view lhs, table_device_view rhs, device_span< detail::dremel_device_view const > l_dremel_device_views, device_span< detail::dremel_device_view const > r_dremel_device_views, cuda::std::optional< device_span< int const >> depth=cuda::std::nullopt, cuda::std::optional< device_span< order const >> column_order=cuda::std::nullopt, cuda::std::optional< device_span< null_order const >> null_precedence=cuda::std::nullopt, PhysicalElementComparator comparator={}) noexcept
Construct a function object for performing a lexicographic comparison between the rows of two tables.
An owning object that can be used to lexicographically compare two rows of the same table.
auto less_equivalent(Nullate nullate={}, PhysicalElementComparator comparator={}) const
Return the binary operator for comparing rows in the table.
self_comparator(table_view const &t, host_span< order const > column_order={}, host_span< null_order const > null_precedence={}, rmm::cuda_stream_view stream=cudf::get_default_stream())
Construct an owning object for performing a lexicographic comparison between two rows of the same tab...
self_comparator(std::shared_ptr< preprocessed_table > t)
Construct an owning object for performing a lexicographic comparison between two rows of the same pre...
auto less(Nullate nullate={}, PhysicalElementComparator comparator={}) const
Return the binary operator for comparing rows in the table.
An owning object that can be used to lexicographically compare rows of two different tables.
two_table_comparator(std::shared_ptr< preprocessed_table > left, std::shared_ptr< preprocessed_table > right)
Construct an owning object for performing a lexicographic comparison between two rows of the same pre...
auto less(Nullate nullate={}, PhysicalElementComparator comparator={}) const
Return the binary operator for comparing rows in the table.
two_table_comparator(table_view const &left, table_view const &right, host_span< order const > column_order={}, host_span< null_order const > null_precedence={}, rmm::cuda_stream_view stream=cudf::get_default_stream())
Construct an owning object for performing a lexicographic comparison between rows of two different ta...
auto less_equivalent(Nullate nullate={}, PhysicalElementComparator comparator={}) const
Return the binary operator for comparing rows in the table.
Table device view that is usable in device memory.
A set of cudf::column_view's of the same size.
Definition: table_view.hpp:200
A set of cudf::column's of the same size.
Definition: table.hpp:40
Column device view class definitions.
uint32_t hash_value_type
Type of hash value.
Definition: hashing.hpp:29
rmm::cuda_stream_view const get_default_stream()
Get the current default stream.
std::unique_ptr< cudf::column > is_valid(cudf::column_view const &input, rmm::cuda_stream_view stream=cudf::get_default_stream(), rmm::device_async_resource_ref mr=cudf::get_current_device_resource_ref())
Creates a column of type_id::BOOL8 elements where for every element in input true indicates the value...
CUDF_HOST_DEVICE constexpr decltype(auto) __forceinline__ type_dispatcher(cudf::data_type dtype, Functor f, Ts &&... args)
Invokes an operator() template with the type instantiation based on the specified cudf::data_type's i...
#define CUDF_EXPECTS(...)
Macro for checking (pre-)conditions that throws an exception when a condition is violated.
Definition: error.hpp:178
null_order
Indicates how null values compare against all other values.
Definition: types.hpp:159
null_equality
Enum to consider two nulls as equal or unequal.
Definition: types.hpp:151
int32_t size_type
Row index type for columns and tables.
Definition: types.hpp:95
#define CUDF_ENABLE_IF(...)
Convenience macro for SFINAE as an unnamed template parameter.
Definition: traits.hpp:50
cuDF interfaces
Definition: host_udf.hpp:37
weak_ordering
Result type of the element_relational_comparator function object.
@ EQUIVALENT
Indicates a is ordered neither before nor after b
auto null_compare(bool lhs_is_null, bool rhs_is_null, null_order null_precedence)
Compare the nulls according to null order.
CUDF_HOST_DEVICE auto make_list_size_iterator(detail::lists_column_device_view const &c)
Makes an iterator that returns size of the list by row index.
Column APIs for sort and rank.
Device version of C++20 std::span with reduced feature set.
Definition: span.hpp:355
A map from cudf::type_id to cudf type that excludes LIST and STRUCT types.
std::conditional_t< t==type_id::STRUCT or t==type_id::LIST, void, id_to_type< t > > type
The type to dispatch to if the type is nested.
Equality comparator functor that compares physical values rather than logical elements like lists,...
constexpr bool operator()(Element const lhs, Element const rhs) const noexcept
Operator for equality comparison of non-floating point values.
Equality comparator functor that compares physical values rather than logical elements like lists,...
constexpr bool operator()(Element const lhs, Element const rhs) const noexcept
Operator for equality comparisons.
Preprocessed table for use with row equality comparison or row hashing.
static std::shared_ptr< preprocessed_table > create(table_view const &table, rmm::cuda_stream_view stream)
Factory to construct preprocessed_table for use with row equality comparison or row hashing.
Wraps and interprets the result of device_row_comparator, true if the result is weak_ordering::LESS m...
less_comparator(Comparator const &comparator)
Constructs a less_comparator.
Wraps and interprets the result of device_row_comparator, true if the result is weak_ordering::LESS o...
less_equivalent_comparator(Comparator const &comparator)
Constructs a less_equivalent_comparator.
Computes a weak ordering of two values with special sorting behavior.
constexpr weak_ordering operator()(Element const lhs, Element const rhs) const noexcept
Operator for relational comparisons.
Preprocessed table for use with lexicographical comparison.
std::invoke_result_t< decltype(table_device_view::create), table_view, rmm::cuda_stream_view > table_device_view_owner
Type of table device view owner for the preprocessed table.
static std::pair< std::shared_ptr< preprocessed_table >, std::shared_ptr< preprocessed_table > > create(table_view const &lhs, table_view const &rhs, host_span< order const > column_order, host_span< null_order const > null_precedence, rmm::cuda_stream_view stream)
Preprocess tables for use with lexicographical comparison.
static std::shared_ptr< preprocessed_table > create(table_view const &table, host_span< order const > column_order, host_span< null_order const > null_precedence, rmm::cuda_stream_view stream)
Preprocess table for use with lexicographical comparison.
Relational comparator functor that compares physical values rather than logical elements like lists,...
constexpr weak_ordering operator()(Element const lhs, Element const rhs) const noexcept
Operator for relational comparison of non-floating point values.
A counting iterator that uses strongly typed indices bound to tables.
thrust::iterator_adaptor< strong_index_iterator< Index >, Index > super_t
The base class.
constexpr strong_index_iterator(Underlying n)
Constructs a strong index iterator.
C++20 std::span with reduced feature set.
Definition: span.hpp:194
Indicates the presence of nulls at compile-time or runtime.
Table device view class definitions.
bool has_nested_columns(table_view const &table)
Determine if any nested columns exist in a given table.
Defines the mapping between cudf::type_id runtime type information and concrete C++ types.
#define CUDF_HOST_DEVICE
Indicates that the function or method is usable on host and device.
Definition: types.hpp:32