File: sort.cpp

package info (click to toggle)
fenics-dolfinx 1%3A0.10.0.post5-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,952 kB
  • sloc: cpp: 36,535; python: 25,391; makefile: 223; sh: 174; xml: 55
file content (134 lines) | stat: -rw-r--r-- 4,670 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Copyright (C) 2021-2025 Igor A. Baratta and Paul T. Kühner
//
// This file is part of DOLFINx (https://www.fenicsproject.org)
//
// SPDX-License-Identifier:    LGPL-3.0-or-later

#include <algorithm>
#include <array>
#include <bitset>
#include <catch2/catch_template_test_macros.hpp>
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <cstdint>
#include <dolfinx/common/sort.h>
#include <functional>
#include <iostream>
#include <limits>
#include <numeric>
#include <random>
#include <type_traits>
#include <vector>

TEMPLATE_TEST_CASE("Test radix sort", "[vector][template]", std::int16_t,
                   std::int32_t, std::int64_t, std::uint16_t, std::uint32_t,
                   std::uint64_t)
{
  auto vec_size = GENERATE(100, 1000, 10000);
  std::vector<TestType> vec;
  vec.reserve(vec_size);

  // Generate a vector of ints with a Uniform Int distribution
  constexpr TestType lower_bound = std::is_signed_v<TestType> ? -10000 : 0;
  std::uniform_int_distribution<TestType> distribution(lower_bound, 10000);
  std::mt19937 engine;
  auto generator = std::bind(distribution, engine);
  std::generate_n(std::back_inserter(vec), vec_size, generator);

  // Sort vector using radix sort
  dolfinx::radix_sort(vec);

  // Check if vector is sorted
  REQUIRE(std::ranges::is_sorted(vec));
}

TEMPLATE_TEST_CASE("Test radix sort (limits)", "[vector][template]",
                   std::int16_t, std::int32_t, std::int64_t, std::uint16_t,
                   std::uint32_t, std::uint64_t)
{
  std::vector<TestType> vec{0, std::numeric_limits<TestType>::max(),
                            std::numeric_limits<TestType>::min()};
  dolfinx::radix_sort(vec);
  REQUIRE(std::ranges::is_sorted(vec));
  REQUIRE(std::ranges::equal(
      vec, std::vector<TestType>{std::numeric_limits<TestType>::min(), 0,
                                 std::numeric_limits<TestType>::max()}));
}

TEMPLATE_TEST_CASE("Test radix sort (projection)", "[radix]", std::int16_t,
                   std::int32_t, std::int64_t, std::uint16_t, std::uint32_t,
                   std::uint64_t)
{
  // Check projection into same type array
  {
    std::vector<TestType> vec = {3, 6, 2, 1, 5, 4, 0};
    if constexpr (std::is_signed_v<TestType>)
    {
      vec[1] *= -1;
      vec[4] *= -1;
    }
    std::vector<TestType> indices(vec.size());
    std::iota(indices.begin(), indices.end(), 0);

    auto proj = [&](auto index) { return vec[index]; };
    dolfinx::radix_sort(indices, proj);
    CHECK(std::ranges::is_sorted(indices, std::less{}, proj));
  }

  // Check projection for non matching value and index type
  {
    std::vector<std::array<TestType, 1>> vec_array{{3}, {6}, {2}, {1},
                                                   {5}, {4}, {0}};
    if constexpr (std::is_signed_v<TestType>)
    {
      vec_array[1][0] *= -1;
      vec_array[4][0] *= -1;
    }
    std::vector<TestType> indices(vec_array.size());
    std::iota(indices.begin(), indices.end(), 0);

    auto proj = [&](auto index) { return vec_array[index][0]; };
    dolfinx::radix_sort(indices, proj);
    CHECK(std::ranges::is_sorted(indices, std::less{}, proj));
  }
}

TEST_CASE("Test argsort bitset")
{
  auto shape0 = GENERATE(100, 1000, 10000);
  constexpr int shape1 = 2;

  std::vector<std::int32_t> arr(shape0 * shape1);

  // Generate a vector of ints with a Uniform Int distribution
  std::uniform_int_distribution<std::int32_t> distribution(0, 10000);
  std::mt19937 engine;
  auto generator = std::bind(distribution, engine);
  std::generate(arr.begin(), arr.end(), generator);

  std::vector<std::int32_t> perm
      = dolfinx::sort_by_perm<std::int32_t>(arr, shape1);
  REQUIRE((int)perm.size() == shape0);

  // Sort by perm using to std::lexicographical_compare
  std::vector<int> index(shape0);
  std::iota(index.begin(), index.end(), 0);
  std::ranges::sort(index,
                    [&arr](int a, int b)
                    {
                      auto it0 = std::next(arr.begin(), shape1 * a);
                      auto it1 = std::next(arr.begin(), shape1 * b);
                      return std::lexicographical_compare(
                          it0, std::next(it0, shape1), it1,
                          std::next(it1, shape1));
                    });

  // Requiring equality of permutation vectors is not a good test, because
  // std::sort is not stable, so we compare the effect on the actual array.
  for (std::size_t i = 0; i < perm.size(); i++)
  {
    REQUIRE(std::equal(arr.data() + shape1 * perm[i],
                       arr.data() + shape1 * perm[i] + shape1,
                       arr.data() + shape1 * index[i]));
  }
}