File indexing completed on 2026-06-07 07:48:41
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/unit_test.hpp>
0010
0011 #include "Acts/Clusterization/Clusterization.hpp"
0012 #include "Acts/Utilities/HashCombine.hpp"
0013 #include "Acts/Utilities/Helpers.hpp"
0014
0015 #include <algorithm>
0016 #include <array>
0017 #include <cstdlib>
0018 #include <iostream>
0019 #include <iterator>
0020 #include <memory>
0021 #include <random>
0022 #include <stdexcept>
0023 #include <utility>
0024 #include <vector>
0025
0026 using namespace Acts;
0027
0028 using Rectangle = std::array<int, 4>;
0029
0030 std::vector<Rectangle> concat(std::vector<std::vector<Rectangle>> vecs) {
0031 std::vector<Rectangle> flat;
0032 for (std::vector<Rectangle>& v : vecs) {
0033 if (flat.empty()) {
0034 flat = std::move(v);
0035 } else {
0036 flat.reserve(flat.size() + v.size());
0037 std::move(v.begin(), v.end(), std::back_inserter(flat));
0038 v.clear();
0039 }
0040 }
0041 return flat;
0042 }
0043
0044 template <typename RNG>
0045 std::vector<Rectangle> segment(int x0, int y0, int x1, int y1, RNG& rng) {
0046
0047 int xmin = x0 + 3;
0048 int xmax = x1 - 3;
0049 int ymin = y0 + 3;
0050 int ymax = y1 - 3;
0051
0052
0053 if (xmax < xmin || ymax < ymin) {
0054 return {{x0, y0, x1, y1}};
0055 }
0056
0057 std::bernoulli_distribution cointoss;
0058 bool splitx = cointoss(rng);
0059 bool splity = cointoss(rng);
0060
0061
0062 if (!(splitx || splity)) {
0063 return {{x0, y0, x1, y1}};
0064 }
0065
0066 int x_ = std::uniform_int_distribution<std::int32_t>(xmin, xmax)(rng);
0067 int y_ = std::uniform_int_distribution<std::int32_t>(ymin, ymax)(rng);
0068
0069 if (splitx && !splity) {
0070 return concat({segment(x0, y0, x_, y1, rng), segment(x_, y0, x1, y1, rng)});
0071 } else if (!splitx && splity) {
0072 return concat({segment(x0, y0, x1, y_, rng), segment(x0, y_, x1, y1, rng)});
0073 } else if (splitx && splity) {
0074 return concat({segment(x0, y0, x_, y_, rng), segment(x_, y0, x1, y_, rng),
0075 segment(x0, y_, x_, y1, rng), segment(x_, y_, x1, y1, rng)});
0076 }
0077 throw std::runtime_error("unreachable");
0078 }
0079
0080 struct Cell2D {
0081 Cell2D(int rowv, int colv) : row(rowv), col(colv) {}
0082 int row, col;
0083 Ccl::Label label{Ccl::NO_LABEL};
0084 };
0085
0086 int getCellRow(const Cell2D& cell) {
0087 return cell.row;
0088 }
0089
0090 int getCellColumn(const Cell2D& cell) {
0091 return cell.col;
0092 }
0093
0094 bool operator==(const Cell2D& left, const Cell2D& right) {
0095 return left.row == right.row && left.col == right.col;
0096 }
0097
0098 bool cellComp(const Cell2D& left, const Cell2D& right) {
0099 return (left.row == right.row) ? left.col < right.col : left.row < right.row;
0100 }
0101
0102 struct Cluster2D {
0103 std::vector<Cell2D> cells;
0104 std::size_t hash{0};
0105 };
0106
0107 void clusterAddCell(Cluster2D& cl, const Cell2D& cell) {
0108 cl.cells.push_back(cell);
0109 }
0110
0111 void hash(Cluster2D& cl) {
0112 std::ranges::sort(cl.cells, cellComp);
0113 cl.hash = 0;
0114 for (const Cell2D& c : cl.cells) {
0115 cl.hash = Acts::hashMixAndCombine(cl.hash, c.col);
0116 }
0117 }
0118
0119 bool clHashComp(const Cluster2D& left, const Cluster2D& right) {
0120 return left.hash < right.hash;
0121 }
0122
0123 template <typename RNG>
0124 void genclusterw(int x, int y, int x0, int y0, int x1, int y1,
0125 std::vector<Cell2D>& cells, RNG& rng, double startp = 0.5,
0126 double decayp = 0.9) {
0127 std::vector<Cell2D> add;
0128
0129 auto maybe_add = [&](int x_, int y_) {
0130 Cell2D c(x_, y_);
0131 if (std::uniform_real_distribution<double>()(rng) < startp &&
0132 !rangeContainsValue(cells, c)) {
0133 cells.push_back(c);
0134 add.push_back(c);
0135 }
0136 };
0137
0138
0139 if (y < y1) {
0140 maybe_add(x, y + 1);
0141 }
0142
0143 if (x < x1 && y < y1) {
0144 maybe_add(x + 1, y + 1);
0145 }
0146
0147 if (x < x1) {
0148 maybe_add(x + 1, y);
0149 }
0150
0151 if (x < x1 && y > y0) {
0152 maybe_add(x + 1, y - 1);
0153 }
0154
0155 if (y > y0) {
0156 maybe_add(x, y - 1);
0157 }
0158
0159 if (x > x0 && y > y0) {
0160 maybe_add(x - 1, y - 1);
0161 }
0162
0163 if (x > x0) {
0164 maybe_add(x - 1, y);
0165 }
0166
0167 if (x > x0 && y < y1) {
0168 maybe_add(x - 1, y + 1);
0169 }
0170
0171 for (Cell2D& c : add) {
0172 genclusterw(c.row, c.col, x0, y0, x1, y1, cells, rng, startp * decayp,
0173 decayp);
0174 }
0175 }
0176
0177 template <typename RNG>
0178 Cluster2D gencluster(int x0, int y0, int x1, int y1, RNG& rng,
0179 double startp = 0.5, double decayp = 0.9) {
0180 int x0_ = x0 + 1;
0181 int x1_ = x1 - 1;
0182 int y0_ = y0 + 1;
0183 int y1_ = y1 - 1;
0184
0185 int x = std::uniform_int_distribution<std::int32_t>(x0_, x1_)(rng);
0186 int y = std::uniform_int_distribution<std::int32_t>(y0_, y1_)(rng);
0187
0188 std::vector<Cell2D> cells = {Cell2D(x, y)};
0189 genclusterw(x, y, x0_, y0_, x1_, y1_, cells, rng, startp, decayp);
0190
0191 Cluster2D cl;
0192 cl.cells = std::move(cells);
0193
0194 return cl;
0195 }
0196
0197 namespace ActsTests {
0198
0199 BOOST_AUTO_TEST_SUITE(ClusterizationSuite)
0200
0201 BOOST_AUTO_TEST_CASE(Grid_2D_rand) {
0202 using Cell = Cell2D;
0203 using CellC = std::vector<Cell>;
0204 using Cluster = Cluster2D;
0205 using ClusterC = std::vector<Cluster>;
0206
0207 std::size_t sizeX = 1000;
0208 std::size_t sizeY = 1000;
0209 std::size_t startSeed = 71902647;
0210 std::size_t ntries = 100;
0211
0212 std::cout << "Grid_2D_rand test with parameters: " << std::endl;
0213 std::cout << " sizeX = " << sizeX << std::endl;
0214 std::cout << " sizeY = " << sizeY << std::endl;
0215 std::cout << " startSeed = " << startSeed << std::endl;
0216 std::cout << " ntries = " << ntries << std::endl;
0217
0218 while (ntries-- > 0) {
0219 std::mt19937_64 rnd(startSeed++);
0220
0221 std::vector<Cluster> cls;
0222 std::vector<Cell> cells;
0223 for (Rectangle& rect : segment(0, 0, sizeX, sizeY, rnd)) {
0224 auto& [x0, y0, x1, y1] = rect;
0225 Cluster cl = gencluster(x0, y0, x1, y1, rnd);
0226 hash(cl);
0227 cells.insert(cells.end(), cl.cells.begin(), cl.cells.end());
0228 cls.push_back(cl);
0229 }
0230
0231 std::shuffle(cells.begin(), cells.end(), rnd);
0232
0233 Ccl::ClusteringData data;
0234 ClusterC newCls;
0235 Ccl::createClusters<CellC, ClusterC>(data, cells, newCls);
0236
0237 for (Cluster& cl : newCls) {
0238 hash(cl);
0239 }
0240
0241 std::ranges::sort(cls, clHashComp);
0242 std::ranges::sort(newCls, clHashComp);
0243
0244 BOOST_CHECK_EQUAL(cls.size(), newCls.size());
0245 for (std::size_t i = 0; i < cls.size(); i++) {
0246 BOOST_CHECK_EQUAL(cls.at(i).hash, newCls.at(i).hash);
0247 }
0248 }
0249 }
0250
0251 BOOST_AUTO_TEST_CASE(Grid_2D_duplicate_cells) {
0252 using Cell = Cell2D;
0253 using CellC = std::vector<Cell>;
0254 using Cluster = Cluster2D;
0255 using ClusterC = std::vector<Cluster>;
0256
0257 CellC cells = {Cell(10, 20), Cell(10, 20)};
0258 ClusterC clusters;
0259
0260 Ccl::ClusteringData data;
0261
0262 BOOST_CHECK_THROW(
0263 (Ccl::createClusters<CellC, ClusterC>(data, cells, clusters)),
0264 std::invalid_argument);
0265 }
0266
0267 BOOST_AUTO_TEST_SUITE_END()
0268
0269 }