Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-11-09 09:28:52

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include <boost/test/unit_test.hpp>
0010 
0011 #include "Acts/Seeding/DoubletSeedFinder.hpp"
0012 
0013 #include <algorithm>
0014 #include <cfenv>
0015 #include <cmath>
0016 #include <cstddef>
0017 #include <limits>
0018 #include <random>
0019 #include <vector>
0020 
0021 using namespace Acts;
0022 
0023 namespace ActsTests {
0024 
0025 namespace {
0026 
0027 using IndexAndCotTheta = DoubletsForMiddleSp::IndexAndCotTheta;
0028 
0029 DoubletsForMiddleSp doubletsOf(const std::vector<float>& cotThetas) {
0030   DoubletsForMiddleSp doublets;
0031   for (std::size_t k = 0; k < cotThetas.size(); ++k) {
0032     doublets.emplace_back(static_cast<SpacePointIndex>(k), cotThetas[k], 1.f,
0033                           1.f, 0.f, 0.f, 0.f, 0.f);
0034   }
0035   return doublets;
0036 }
0037 
0038 /// Checks that @p sorted holds the doublets of @p range in the order
0039 /// std::ranges::stable_sort gives, with the sign of each cotTheta kept (which
0040 /// tells -0 from +0).
0041 void checkSorted(const std::vector<float>& cotThetas,
0042                  const DoubletsForMiddleSp::IndexRange& range,
0043                  const std::vector<IndexAndCotTheta>& sorted) {
0044   std::vector<IndexAndCotTheta> expected;
0045   for (auto i = range.first; i < range.second; ++i) {
0046     expected.push_back({i, cotThetas[i]});
0047   }
0048   std::ranges::stable_sort(expected, {}, &IndexAndCotTheta::cotTheta);
0049 
0050   std::vector<DoubletsForMiddleSp::Index> expectedIndices;
0051   std::vector<DoubletsForMiddleSp::Index> sortedIndices;
0052   std::vector<bool> expectedSigns;
0053   std::vector<bool> sortedSigns;
0054   for (const IndexAndCotTheta& item : expected) {
0055     expectedIndices.push_back(item.index);
0056     expectedSigns.push_back(std::signbit(item.cotTheta));
0057   }
0058   for (const IndexAndCotTheta& item : sorted) {
0059     sortedIndices.push_back(item.index);
0060     sortedSigns.push_back(std::signbit(item.cotTheta));
0061   }
0062 
0063   BOOST_CHECK_EQUAL_COLLECTIONS(sortedIndices.begin(), sortedIndices.end(),
0064                                 expectedIndices.begin(), expectedIndices.end());
0065   BOOST_CHECK_EQUAL_COLLECTIONS(sortedSigns.begin(), sortedSigns.end(),
0066                                 expectedSigns.begin(), expectedSigns.end());
0067 }
0068 
0069 /// Sorts @p range of @p doublets into @p sorted, and checks that no floating
0070 /// point exception was raised: it would trap where they are enabled.
0071 void sortWithoutFpe(const DoubletsForMiddleSp& doublets,
0072                     const DoubletsForMiddleSp::IndexRange& range,
0073                     std::vector<IndexAndCotTheta>& sorted,
0074                     DoubletsForMiddleSp::SortStorage* storage) {
0075   std::feclearexcept(FE_ALL_EXCEPT);
0076   if (storage != nullptr) {
0077     doublets.sortByCotTheta(range, sorted, *storage);
0078   } else {
0079     doublets.sortByCotTheta(range, sorted);
0080   }
0081   BOOST_CHECK(!std::fetestexcept(FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW));
0082 }
0083 
0084 /// Sorts all of @p cotThetas, reusing @p storage, and checks the result.
0085 void checkSort(const std::vector<float>& cotThetas,
0086                DoubletsForMiddleSp::SortStorage& storage) {
0087   const DoubletsForMiddleSp doublets = doubletsOf(cotThetas);
0088   const DoubletsForMiddleSp::IndexRange range{0, doublets.size()};
0089   std::vector<IndexAndCotTheta> sorted;
0090   sortWithoutFpe(doublets, range, sorted, &storage);
0091   checkSorted(cotThetas, range, sorted);
0092 }
0093 
0094 std::vector<float> uniformCotThetas(std::size_t n, std::mt19937& rng) {
0095   std::uniform_real_distribution<float> uniform(-5.f, 5.f);
0096   std::vector<float> cotThetas(n);
0097   for (float& cotTheta : cotThetas) {
0098     cotTheta = uniform(rng);
0099   }
0100   return cotThetas;
0101 }
0102 
0103 }  // namespace
0104 
0105 BOOST_AUTO_TEST_SUITE(SeedingSuite)
0106 
0107 BOOST_AUTO_TEST_CASE(SortByCotThetaMatchesStableSort) {
0108   std::mt19937 rng(42);
0109   // One storage for every call, of whatever size the last call left it
0110   DoubletsForMiddleSp::SortStorage storage;
0111   // Sizes on both sides of where the sort changes
0112   for (std::size_t n : {0,  1,  2,  3,  4,  5,  6,  7,  8,   9,    10,   15,
0113                         16, 17, 31, 32, 33, 40, 64, 65, 100, 1024, 3000, 33}) {
0114     checkSort(uniformCotThetas(n, rng), storage);
0115   }
0116 }
0117 
0118 BOOST_AUTO_TEST_CASE(SortByCotThetaKeepsOrderOfEqualCotTheta) {
0119   std::mt19937 rng(7);
0120   std::uniform_int_distribution<int> fewValues(-3, 3);
0121   DoubletsForMiddleSp::SortStorage storage;
0122   for (std::size_t n : {5, 10, 40, 41, 500, 2000}) {
0123     std::vector<float> cotThetas(n);
0124     for (float& cotTheta : cotThetas) {
0125       cotTheta = static_cast<float>(fewValues(rng));
0126     }
0127     checkSort(cotThetas, storage);
0128   }
0129 
0130   // All equal
0131   for (std::size_t n : {6, 30, 300}) {
0132     checkSort(std::vector<float>(n, 1.f), storage);
0133   }
0134 }
0135 
0136 BOOST_AUTO_TEST_CASE(SortByCotThetaKeepsSignedZeros) {
0137   DoubletsForMiddleSp::SortStorage storage;
0138   for (std::size_t n : {6, 20, 200}) {
0139     std::vector<float> cotThetas(n);
0140     for (std::size_t k = 0; k < n; ++k) {
0141       if (k % 3 == 0) {
0142         cotThetas[k] = -0.f;
0143       } else if (k % 3 == 1) {
0144         cotThetas[k] = 0.f;
0145       } else {
0146         cotThetas[k] = 1.f - 0.01f * static_cast<float>(k);
0147       }
0148     }
0149     checkSort(cotThetas, storage);
0150   }
0151 }
0152 
0153 BOOST_AUTO_TEST_CASE(SortByCotThetaFallsBackForInfiniteCotTheta) {
0154   const float inf = std::numeric_limits<float>::infinity();
0155   DoubletsForMiddleSp::SortStorage storage;
0156   for (std::size_t n : {6, 20, 200, 1000}) {
0157     std::vector<float> cotThetas(n);
0158     for (std::size_t k = 0; k < n; ++k) {
0159       cotThetas[k] = static_cast<float>(n - k);
0160     }
0161     cotThetas[n / 2] = inf;
0162     cotThetas[n / 3] = -inf;
0163     checkSort(cotThetas, storage);
0164   }
0165 }
0166 
0167 BOOST_AUTO_TEST_CASE(SortByCotThetaFallsBackForTinyRange) {
0168   // Too close together to spread over buckets
0169   const float smallest = std::numeric_limits<float>::denorm_min();
0170   DoubletsForMiddleSp::SortStorage storage;
0171   for (std::size_t n : {6, 20, 200, 1000}) {
0172     std::vector<float> cotThetas(n);
0173     for (std::size_t k = 0; k < n; ++k) {
0174       cotThetas[k] = (k % 2 == 0) ? 0.f : smallest;
0175     }
0176     checkSort(cotThetas, storage);
0177   }
0178 }
0179 
0180 BOOST_AUTO_TEST_CASE(SortByCotThetaHandlesOneOutlier) {
0181   // Almost every doublet in one bucket
0182   std::mt19937 rng(11);
0183   std::uniform_real_distribution<float> narrow(-0.005f, 0.005f);
0184   DoubletsForMiddleSp::SortStorage storage;
0185   for (std::size_t n : {6, 20, 200, 1000}) {
0186     std::vector<float> cotThetas(n);
0187     for (float& cotTheta : cotThetas) {
0188       cotTheta = narrow(rng);
0189     }
0190     cotThetas[n / 2] = 27.f;
0191     checkSort(cotThetas, storage);
0192   }
0193 }
0194 
0195 BOOST_AUTO_TEST_CASE(SortByCotThetaSortsRangesWithBothOverloads) {
0196   std::mt19937 rng(5);
0197   for (std::size_t n : {0, 5, 30, 300}) {
0198     const std::vector<float> cotThetas = uniformCotThetas(n + 20, rng);
0199     const DoubletsForMiddleSp doublets = doubletsOf(cotThetas);
0200 
0201     // A range that does not start at the first doublet
0202     const DoubletsForMiddleSp::IndexRange range{
0203         10, static_cast<DoubletsForMiddleSp::Index>(10 + n)};
0204 
0205     std::vector<IndexAndCotTheta> allocating;
0206     sortWithoutFpe(doublets, range, allocating, nullptr);
0207     checkSorted(cotThetas, range, allocating);
0208 
0209     std::vector<IndexAndCotTheta> withStorage;
0210     DoubletsForMiddleSp::SortStorage storage;
0211     sortWithoutFpe(doublets, range, withStorage, &storage);
0212     checkSorted(cotThetas, range, withStorage);
0213   }
0214 }
0215 
0216 BOOST_AUTO_TEST_SUITE_END()
0217 
0218 }  // namespace ActsTests