The multiplicative spread of the outlier band is the weighted median of (|ln(I/median)|, weight) pairs, one per usable full, and its sort was the bulk of that step. SortPairsGPU sorts them on the GPU (by the second key, then stably by the first) and leaves the sequence std::sort leaves: two pairs that compare equal are the same two numbers. The values themselves (a logarithm) and the running sum stay on the host. p.mtz md5 unchanged on myob, cytc, 8a1a and 8qaw (GPU build); a Catch2 case checks the device order against std::sort. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SVmAWnzCmRKAXVUCdc4iNi
34 lines
1.1 KiB
C++
34 lines
1.1 KiB
C++
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#include <catch2/catch_all.hpp>
|
|
|
|
#ifdef JFJOCH_USE_CUDA
|
|
#include <algorithm>
|
|
#include <random>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include "../common/CUDAWrapper.h"
|
|
#include "../image_analysis/scale_merge/PairSortGPU.h"
|
|
|
|
// The device sort against std::sort, with many repeated first and second values (so both keys decide)
|
|
// and whole duplicate pairs.
|
|
TEST_CASE("SortPairsGPU: the order std::sort leaves", "[PairSortGPU]") {
|
|
if (get_gpu_count() == 0) {
|
|
WARN("No CUDA GPU present. Skipping SortPairsGPU");
|
|
return;
|
|
}
|
|
std::mt19937 rng(5);
|
|
std::uniform_int_distribution<int> coarse(0, 200);
|
|
std::exponential_distribution<double> fine(1.0);
|
|
std::vector<std::pair<double, double>> v(500000);
|
|
for (auto &p : v) p = {coarse(rng) * 0.37, coarse(rng) % 3 ? fine(rng) : 0.5 * coarse(rng)};
|
|
for (size_t i = 0; i < 1000; ++i) v[v.size() - 1 - i] = v[i];
|
|
auto host = v;
|
|
std::sort(host.begin(), host.end());
|
|
SortPairsGPU(v);
|
|
CHECK(v == host);
|
|
}
|
|
#endif
|