File indexing completed on 2026-10-16 08:28:59
0001 #include "PHSiliconSeedPrunerHelper.h"
0002
0003 #include <trackbase/TrkrDefs.h>
0004
0005 #include <trackbase_historic/TrackSeed.h>
0006 #include <trackbase_historic/TrackSeedContainer.h>
0007
0008 #include <gsl/gsl_randist.h>
0009
0010 #include <algorithm>
0011 #include <iostream>
0012 #include <map>
0013 #include <numeric>
0014 #include <unordered_map>
0015 #include <vector>
0016
0017 namespace PHSiliconSeedPrunerHelper
0018 {
0019
0020 struct SeedRecord
0021 {
0022 size_t index = 0;
0023 std::vector<TrkrDefs::cluskey> allClusterKeys;
0024 std::vector<TrkrDefs::cluskey> mvtxClusterKeys;
0025 bool completeMvtx = false;
0026 };
0027
0028 using ConflictGraph = std::vector<std::vector<size_t>>;
0029
0030
0031 class DisjointSet
0032 {
0033 public:
0034 explicit DisjointSet(size_t n)
0035 : parent_(n)
0036 , rank_(n, 0)
0037 {
0038 std::iota(parent_.begin(), parent_.end(), 0);
0039 }
0040
0041 size_t Find(size_t value)
0042 {
0043
0044 if (parent_[value] != value)
0045 {
0046 parent_[value] = Find(parent_[value]);
0047 }
0048 return parent_[value];
0049 }
0050
0051
0052 void Union(size_t lhs, size_t rhs)
0053 {
0054 size_t root_lhs = Find(lhs);
0055 size_t root_rhs = Find(rhs);
0056 if (root_lhs == root_rhs)
0057 {
0058 return;
0059 }
0060
0061
0062 if (rank_[root_lhs] < rank_[root_rhs])
0063 {
0064 std::swap(root_lhs, root_rhs);
0065 }
0066
0067
0068 parent_[root_rhs] = root_lhs;
0069 if (rank_[root_lhs] == rank_[root_rhs])
0070 {
0071 ++rank_[root_lhs];
0072 }
0073 }
0074
0075 private:
0076 std::vector<size_t> parent_;
0077 std::vector<unsigned int> rank_;
0078 };
0079
0080 SeedRecord MakeSeedRecord(size_t seedIndex, const TrackSeed &seed, size_t mvtxLayerCount)
0081 {
0082 SeedRecord record;
0083 record.index = seedIndex;
0084
0085 std::vector<unsigned int> mvtx_layer_counts(mvtxLayerCount, 0);
0086 for (auto iter = seed.begin_cluster_keys(); iter != seed.end_cluster_keys(); ++iter)
0087 {
0088 const TrkrDefs::cluskey key = *iter;
0089 record.allClusterKeys.push_back(key);
0090
0091 if (TrkrDefs::getTrkrId(key) != TrkrDefs::mvtxId)
0092 {
0093 continue;
0094 }
0095
0096 record.mvtxClusterKeys.push_back(key);
0097 const unsigned int layer = TrkrDefs::getLayer(key);
0098 if (layer < mvtxLayerCount)
0099 {
0100 ++mvtx_layer_counts[layer];
0101 }
0102 }
0103
0104 record.completeMvtx =
0105 record.mvtxClusterKeys.size() == mvtxLayerCount &&
0106 mvtx_layer_counts[0] == 1 &&
0107 mvtx_layer_counts[1] == 1 &&
0108 mvtx_layer_counts[2] == 1;
0109 return record;
0110 }
0111
0112
0113
0114 std::vector<std::vector<size_t>> ConnectedComponents(const ConflictGraph &conflict)
0115 {
0116 std::vector<std::vector<size_t>> components;
0117 std::vector<bool> visited(conflict.size(), false);
0118
0119 for (size_t start = 0; start < conflict.size(); ++start)
0120 {
0121 if (visited[start])
0122 {
0123 continue;
0124 }
0125
0126 std::vector<size_t> component;
0127 std::vector<size_t> stack{start};
0128 visited[start] = true;
0129
0130 while (!stack.empty())
0131 {
0132 const size_t vertex = stack.back();
0133 stack.pop_back();
0134 component.push_back(vertex);
0135
0136
0137 for (const size_t neighbor : conflict[vertex])
0138 {
0139 if (!visited[neighbor])
0140 {
0141 visited[neighbor] = true;
0142 stack.push_back(neighbor);
0143 }
0144 }
0145 }
0146
0147 components.push_back(std::move(component));
0148 }
0149
0150 return components;
0151 }
0152
0153
0154 struct TripletClass
0155 {
0156 std::vector<TrkrDefs::cluskey> mvtxKeys;
0157 std::vector<size_t> memberSeedIndices;
0158 };
0159
0160
0161 std::vector<TripletClass> CollapseDuplicateTriplets(
0162 const std::vector<const SeedRecord *> &completeSeeds,
0163 bool debugVerbosity)
0164 {
0165 if (debugVerbosity)
0166 {
0167 std::cout << __func__ << " : " << __LINE__ << " : collapsing " << completeSeeds.size() << " complete MVTX seeds into unique MVTX-triplet classes" << std::endl;
0168 }
0169
0170 std::map<std::vector<TrkrDefs::cluskey>, size_t> classByTriplet;
0171 std::vector<TripletClass> classes;
0172
0173 for (const SeedRecord *seed : completeSeeds)
0174 {
0175 const auto inserted = classByTriplet.emplace(seed->mvtxClusterKeys, classes.size());
0176
0177 if (inserted.second)
0178 {
0179 TripletClass cls;
0180 cls.mvtxKeys = seed->mvtxClusterKeys;
0181 classes.push_back(std::move(cls));
0182 }
0183 classes[inserted.first->second].memberSeedIndices.push_back(seed->index);
0184 }
0185
0186 return classes;
0187 }
0188
0189
0190 ConflictGraph BuildClassConflictGraph(const std::vector<TripletClass> &classes)
0191 {
0192
0193 std::unordered_map<TrkrDefs::cluskey, std::vector<size_t>> classesByKey;
0194 for (size_t c = 0; c < classes.size(); ++c)
0195 {
0196 for (const TrkrDefs::cluskey key : classes[c].mvtxKeys)
0197 {
0198 classesByKey[key].push_back(c);
0199 }
0200 }
0201
0202
0203 ConflictGraph conflict(classes.size());
0204 for (const auto &entry : classesByKey)
0205 {
0206 const std::vector<size_t> &sharing = entry.second;
0207 for (size_t a = 0; a < sharing.size(); ++a)
0208 {
0209 for (size_t b = a + 1; b < sharing.size(); ++b)
0210 {
0211
0212 conflict[sharing[a]].push_back(sharing[b]);
0213 conflict[sharing[b]].push_back(sharing[a]);
0214 }
0215 }
0216 }
0217
0218
0219 for (std::vector<size_t> &neighbors : conflict)
0220 {
0221 std::sort(neighbors.begin(), neighbors.end());
0222 neighbors.erase(std::unique(neighbors.begin(), neighbors.end()), neighbors.end());
0223 }
0224
0225 return conflict;
0226 }
0227
0228
0229 struct ComponentProblem
0230 {
0231 size_t m = 0;
0232 size_t numKeys = 0;
0233 std::vector<std::vector<size_t>> adj;
0234 std::vector<std::vector<size_t>> keyIds;
0235 std::vector<std::vector<size_t>> keyToClasses;
0236 std::vector<double> memberCount;
0237 std::vector<size_t> globalClassId;
0238 };
0239
0240 ComponentProblem BuildComponentProblem(const std::vector<TripletClass> &classes, const ConflictGraph &classConflict, const std::vector<size_t> &component)
0241 {
0242 ComponentProblem prob;
0243 prob.m = component.size();
0244 prob.globalClassId = component;
0245 prob.adj.resize(prob.m);
0246 prob.keyIds.resize(prob.m);
0247 prob.memberCount.resize(prob.m);
0248
0249 std::unordered_map<size_t, size_t> localOf;
0250 for (size_t local = 0; local < component.size(); ++local)
0251 {
0252 localOf[component[local]] = local;
0253 }
0254
0255 std::unordered_map<TrkrDefs::cluskey, size_t> keyId;
0256
0257 for (size_t local = 0; local < prob.m; ++local)
0258 {
0259 const size_t global = component[local];
0260 prob.memberCount[local] = static_cast<double>(classes[global].memberSeedIndices.size());
0261
0262 for (const TrkrDefs::cluskey key : classes[global].mvtxKeys)
0263 {
0264 const auto inserted = keyId.emplace(key, keyId.size());
0265 prob.keyIds[local].push_back(inserted.first->second);
0266 }
0267
0268 for (const size_t global_neighbor : classConflict[global])
0269 {
0270 const auto it = localOf.find(global_neighbor);
0271 if (it != localOf.end())
0272 {
0273 prob.adj[local].push_back(it->second);
0274 }
0275 }
0276 }
0277
0278 prob.numKeys = keyId.size();
0279 prob.keyToClasses.assign(prob.numKeys, {});
0280 for (size_t local = 0; local < prob.m; ++local)
0281 {
0282 for (const size_t kid : prob.keyIds[local])
0283 {
0284 prob.keyToClasses[kid].push_back(local);
0285 }
0286 }
0287
0288 return prob;
0289 }
0290
0291
0292 class ComponentSolver
0293 {
0294 public:
0295 explicit ComponentSolver(
0296 const ComponentProblem &problem,
0297 bool debugDetailed = false,
0298 size_t debugComponentIndex = 0
0299 )
0300 : prob_(problem)
0301 ,
0302 active_(problem.m, true)
0303 ,
0304 scratchKeyCount_(problem.numKeys, 0)
0305 ,
0306 covered_(problem.m, false)
0307 ,
0308 debugDetailed_(debugDetailed)
0309 ,
0310 debugComponentIndex_(debugComponentIndex)
0311 {
0312 }
0313
0314
0315
0316 bool SolveBudgeted(size_t budget)
0317 {
0318 std::fill(active_.begin(), active_.end(), true);
0319 bestCardinality_ = 0;
0320 bestSet_.clear();
0321 budget_ = budget;
0322 nodes_ = 0;
0323 budgetHit_ = false;
0324 debugDepth_ = 0;
0325 std::vector<size_t> chosen;
0326 chosen.reserve(prob_.m);
0327 if (debugDetailed_)
0328 {
0329 std::cout << __func__ << " : " << __LINE__ << " : component " << debugComponentIndex_ << " starts exact independent-set search with state budget " << budget_ << std::endl;
0330 }
0331 SearchCardinality(chosen);
0332 return !budgetHit_;
0333 }
0334
0335 size_t BestCardinality() const { return bestCardinality_; }
0336 const std::vector<size_t> &BestSet() const { return bestSet_; }
0337
0338
0339
0340 std::vector<std::vector<size_t>> CollectRepresentatives(size_t target, size_t maxReps, size_t budget)
0341 {
0342 std::fill(active_.begin(), active_.end(), true);
0343 target_ = target;
0344 maxReps_ = (maxReps < 1) ? 1 : maxReps;
0345 reps_.clear();
0346 budget_ = budget;
0347 nodes_ = 0;
0348 budgetHit_ = false;
0349 std::vector<size_t> chosen;
0350 chosen.reserve(target_);
0351 SearchRepresentatives(chosen);
0352 return reps_;
0353 }
0354
0355
0356 std::vector<size_t> BuildHeuristicSet(size_t restarts, gsl_rng *rng) const
0357 {
0358 std::vector<bool> bestInSet(prob_.m, false);
0359 size_t bestSize = 0;
0360
0361
0362 {
0363 std::vector<bool> inSet(prob_.m, false);
0364 for (const size_t v : bestSet_)
0365 {
0366 inSet[v] = true;
0367 }
0368 PolishInSet(inSet);
0369 const size_t sz = CountInSet(inSet);
0370 if (sz > bestSize)
0371 {
0372 bestSize = sz;
0373 bestInSet = inSet;
0374 }
0375 }
0376
0377
0378 std::vector<size_t> order(prob_.m);
0379 std::iota(order.begin(), order.end(), 0);
0380 for (size_t t = 0; t < restarts; ++t)
0381 {
0382 gsl_ran_shuffle(rng, order.data(), order.size(), sizeof(size_t));
0383 std::vector<bool> inSet = GreedyFromOrder(order);
0384 PolishInSet(inSet);
0385 const size_t sz = CountInSet(inSet);
0386 if (sz > bestSize)
0387 {
0388 bestSize = sz;
0389 bestInSet = inSet;
0390 }
0391 }
0392
0393 std::vector<size_t> out;
0394 for (size_t v = 0; v < prob_.m; ++v)
0395 {
0396 if (bestInSet[v])
0397 {
0398 out.push_back(v);
0399 }
0400 }
0401 return out;
0402 }
0403
0404 private:
0405 size_t CountActive() const
0406 {
0407 size_t count = 0;
0408 for (size_t v = 0; v < prob_.m; ++v)
0409 {
0410 count += active_[v];
0411 }
0412 return count;
0413 }
0414
0415 static size_t CountInSet(const std::vector<bool> &inSet)
0416 {
0417 size_t count = 0;
0418 for (const bool x : inSet)
0419 {
0420 count += x;
0421 }
0422 return count;
0423 }
0424
0425
0426
0427 std::vector<bool> GreedyFromOrder(const std::vector<size_t> &order) const
0428 {
0429 std::vector<bool> inSet(prob_.m, false);
0430 std::vector<bool> blocked(prob_.m, false);
0431 for (const size_t v : order)
0432 {
0433 if (blocked[v])
0434 {
0435 continue;
0436 }
0437 inSet[v] = true;
0438 for (const size_t u : prob_.adj[v])
0439 {
0440 blocked[u] = true;
0441 }
0442 }
0443 return inSet;
0444 }
0445
0446
0447
0448 void PolishInSet(std::vector<bool> &inSet) const
0449 {
0450 bool improved = true;
0451 const size_t passLimit = 1000;
0452 size_t pass = 0;
0453 while (improved && pass++ < passLimit)
0454 {
0455 improved = false;
0456 for (size_t y = 0; y < prob_.m && !improved; ++y)
0457 {
0458 if (!inSet[y])
0459 {
0460 continue;
0461 }
0462
0463 std::vector<size_t> candidates;
0464 for (const size_t x : prob_.adj[y])
0465 {
0466 if (inSet[x])
0467 {
0468 continue;
0469 }
0470 size_t inSetNeighbors = 0;
0471 size_t onlyNeighbor = 0;
0472 for (const size_t u : prob_.adj[x])
0473 {
0474 if (inSet[u])
0475 {
0476 ++inSetNeighbors;
0477 onlyNeighbor = u;
0478 }
0479 }
0480 if (inSetNeighbors == 1 && onlyNeighbor == y)
0481 {
0482 candidates.push_back(x);
0483 }
0484 }
0485
0486
0487 for (size_t a = 0; a < candidates.size() && !improved; ++a)
0488 {
0489 for (size_t b = a + 1; b < candidates.size(); ++b)
0490 {
0491 bool adjacent = false;
0492 for (const size_t u : prob_.adj[candidates[a]])
0493 {
0494 if (u == candidates[b])
0495 {
0496 adjacent = true;
0497 break;
0498 }
0499 }
0500 if (!adjacent)
0501 {
0502 inSet[y] = false;
0503 inSet[candidates[a]] = true;
0504 inSet[candidates[b]] = true;
0505 improved = true;
0506 break;
0507 }
0508 }
0509 }
0510 }
0511 }
0512 }
0513
0514
0515
0516 size_t CliqueCoverBound()
0517 {
0518 std::fill(scratchKeyCount_.begin(), scratchKeyCount_.end(), 0);
0519 size_t remaining = 0;
0520 for (size_t v = 0; v < prob_.m; ++v)
0521 {
0522 if (!active_[v])
0523 {
0524 continue;
0525 }
0526 ++remaining;
0527 covered_[v] = false;
0528 for (const size_t kid : prob_.keyIds[v])
0529 {
0530 ++scratchKeyCount_[kid];
0531 }
0532 }
0533 if (remaining == 0)
0534 {
0535 return 0;
0536 }
0537
0538 size_t cover = 0;
0539 while (remaining > 0)
0540 {
0541 size_t bestKey = 0;
0542 size_t bestCount = 0;
0543 for (size_t kid = 0; kid < prob_.numKeys; ++kid)
0544 {
0545 if (scratchKeyCount_[kid] > bestCount)
0546 {
0547 bestCount = scratchKeyCount_[kid];
0548 bestKey = kid;
0549 }
0550 }
0551 if (bestCount == 0)
0552 {
0553 break;
0554 }
0555
0556 ++cover;
0557 for (const size_t v : prob_.keyToClasses[bestKey])
0558 {
0559 if (!active_[v] || covered_[v])
0560 {
0561 continue;
0562 }
0563 covered_[v] = true;
0564 --remaining;
0565 for (const size_t kid : prob_.keyIds[v])
0566 {
0567 if (scratchKeyCount_[kid] > 0)
0568 {
0569 --scratchKeyCount_[kid];
0570 }
0571 }
0572 }
0573 }
0574
0575 return cover;
0576 }
0577
0578
0579
0580 size_t ChooseBranchVertex() const
0581 {
0582 size_t bestVertex = 0;
0583 size_t bestDegree = 0;
0584 bool found = false;
0585 for (size_t v = 0; v < prob_.m; ++v)
0586 {
0587 if (!active_[v])
0588 {
0589 continue;
0590 }
0591 size_t degree = 0;
0592 for (const size_t neighbor : prob_.adj[v])
0593 {
0594 degree += active_[neighbor];
0595 }
0596 if (!found || degree > bestDegree)
0597 {
0598 found = true;
0599 bestDegree = degree;
0600 bestVertex = v;
0601 }
0602 }
0603 return bestVertex;
0604 }
0605
0606 void FoldIsolated(std::vector<size_t> &chosen, std::vector<size_t> &folded)
0607 {
0608 for (size_t v = 0; v < prob_.m; ++v)
0609 {
0610 if (!active_[v])
0611 {
0612 continue;
0613 }
0614 bool hasActiveNeighbor = false;
0615 for (const size_t neighbor : prob_.adj[v])
0616 {
0617 if (active_[neighbor])
0618 {
0619 hasActiveNeighbor = true;
0620 break;
0621 }
0622 }
0623 if (!hasActiveNeighbor)
0624 {
0625 folded.push_back(v);
0626 }
0627 }
0628 for (const size_t v : folded)
0629 {
0630 active_[v] = false;
0631 chosen.push_back(v);
0632 }
0633 }
0634
0635 void Unfold(std::vector<size_t> &chosen, const std::vector<size_t> &folded)
0636 {
0637 for (size_t i = folded.size(); i-- > 0;)
0638 {
0639 active_[folded[i]] = true;
0640 chosen.pop_back();
0641 }
0642 }
0643
0644 void Deactivate(size_t vertex, std::vector<size_t> &deactivated)
0645 {
0646 active_[vertex] = false;
0647 deactivated.push_back(vertex);
0648 for (const size_t neighbor : prob_.adj[vertex])
0649 {
0650 if (active_[neighbor])
0651 {
0652 active_[neighbor] = false;
0653 deactivated.push_back(neighbor);
0654 }
0655 }
0656 }
0657
0658 void Reactivate(const std::vector<size_t> &deactivated)
0659 {
0660 for (const size_t vertex : deactivated)
0661 {
0662 active_[vertex] = true;
0663 }
0664 }
0665
0666 void SearchCardinality(std::vector<size_t> &chosen)
0667 {
0668 if (budgetHit_)
0669 {
0670 return;
0671 }
0672 if (++nodes_ > budget_)
0673 {
0674 budgetHit_ = true;
0675 if (debugDetailed_)
0676 {
0677 std::cout << __func__ << " : " << __LINE__ << " : component " << debugComponentIndex_ << " budget hit at node " << nodes_ << " with best size " << bestCardinality_ << std::endl;
0678 }
0679 return;
0680 }
0681
0682 const size_t activeBeforeFold = CountActive();
0683 if (debugDetailed_)
0684 {
0685 std::cout << __func__ << " : " << __LINE__ << " : component " << debugComponentIndex_ << " node " << nodes_ << " depth " << debugDepth_ << " enters with chosen=" << chosen.size()
0686 << ", active=" << activeBeforeFold << ", best=" << bestCardinality_ << std::endl;
0687 }
0688
0689 std::vector<size_t> folded;
0690 FoldIsolated(chosen, folded);
0691 if (debugDetailed_ && !folded.empty())
0692 {
0693 std::cout << __func__ << " : " << __LINE__ << " : component " << debugComponentIndex_ << " folded isolated class(es)";
0694 for (const size_t v : folded)
0695 {
0696 std::cout << " " << v;
0697 }
0698 std::cout << "; chosen is now " << chosen.size() << std::endl;
0699 }
0700
0701 const size_t activeAfterFold = CountActive();
0702 if (activeAfterFold == 0)
0703 {
0704 if (chosen.size() > bestCardinality_)
0705 {
0706 bestCardinality_ = chosen.size();
0707 bestSet_ = chosen;
0708 if (debugDetailed_)
0709 {
0710 std::cout << __func__ << " : " << __LINE__ << " : component " << debugComponentIndex_ << " updates best set to size " << bestCardinality_ << " at a closed leaf" << std::endl;
0711 }
0712 }
0713 else if (debugDetailed_)
0714 {
0715 std::cout << __func__ << " : " << __LINE__ << " : component " << debugComponentIndex_ << " closed leaf has size " << chosen.size() << " and does not improve best " << bestCardinality_
0716 << std::endl;
0717 }
0718 Unfold(chosen, folded);
0719 return;
0720 }
0721
0722 const size_t cover = CliqueCoverBound();
0723 if (debugDetailed_)
0724 {
0725 std::cout << __func__ << " : " << __LINE__ << " : component " << debugComponentIndex_ << " clique-cover upper bound = " << cover << "; chosen + bound = " << chosen.size() + cover
0726 << std::endl;
0727 }
0728 if (chosen.size() + cover <= bestCardinality_)
0729 {
0730 if (debugDetailed_)
0731 {
0732 std::cout << __func__ << " : " << __LINE__ << " : component " << debugComponentIndex_ << " prunes branch because " << chosen.size() + cover << " <= current best " << bestCardinality_
0733 << std::endl;
0734 }
0735 Unfold(chosen, folded);
0736 return;
0737 }
0738 if (cover == 1)
0739 {
0740 if (chosen.size() + 1 > bestCardinality_)
0741 {
0742 size_t pick = prob_.m;
0743 for (size_t v = 0; v < prob_.m; ++v)
0744 {
0745 if (active_[v])
0746 {
0747 pick = v;
0748 break;
0749 }
0750 }
0751 bestCardinality_ = chosen.size() + 1;
0752 bestSet_ = chosen;
0753 if (pick < prob_.m)
0754 {
0755 bestSet_.push_back(pick);
0756 }
0757 if (debugDetailed_)
0758 {
0759 std::cout << __func__ << " : " << __LINE__ << " : component " << debugComponentIndex_ << " single-clique shortcut picks class " << pick << " and updates best size to "
0760 << bestCardinality_ << std::endl;
0761 }
0762 }
0763 else if (debugDetailed_)
0764 {
0765 std::cout << __func__ << " : " << __LINE__ << " : component " << debugComponentIndex_ << " single-clique shortcut cannot improve best " << bestCardinality_ << std::endl;
0766 }
0767 Unfold(chosen, folded);
0768 return;
0769 }
0770
0771 const size_t vertex = ChooseBranchVertex();
0772 if (debugDetailed_)
0773 {
0774 std::cout << __func__ << " : " << __LINE__ << " : component " << debugComponentIndex_ << " branches on class " << vertex << std::endl;
0775 }
0776
0777 std::vector<size_t> deactivated;
0778 Deactivate(vertex, deactivated);
0779 if (debugDetailed_)
0780 {
0781 std::cout << __func__ << " : " << __LINE__ << " : component " << debugComponentIndex_ << " include class " << vertex << " and deactivate";
0782 for (const size_t v : deactivated)
0783 {
0784 std::cout << " " << v;
0785 }
0786 std::cout << std::endl;
0787 }
0788 chosen.push_back(vertex);
0789 ++debugDepth_;
0790 SearchCardinality(chosen);
0791 --debugDepth_;
0792 chosen.pop_back();
0793 Reactivate(deactivated);
0794
0795 active_[vertex] = false;
0796 if (debugDetailed_)
0797 {
0798 std::cout << __func__ << " : " << __LINE__ << " : component " << debugComponentIndex_ << " exclude class " << vertex << " and continue residual search" << std::endl;
0799 }
0800 ++debugDepth_;
0801 SearchCardinality(chosen);
0802 --debugDepth_;
0803 active_[vertex] = true;
0804
0805 Unfold(chosen, folded);
0806 }
0807
0808 void Record(const std::vector<size_t> &chosen)
0809 {
0810 if (reps_.size() >= maxReps_)
0811 {
0812 return;
0813 }
0814 std::vector<size_t> sorted = chosen;
0815 std::sort(sorted.begin(), sorted.end());
0816 for (const std::vector<size_t> &existing : reps_)
0817 {
0818 if (existing == sorted)
0819 {
0820 return;
0821 }
0822 }
0823 reps_.push_back(std::move(sorted));
0824 }
0825
0826
0827
0828
0829 void SearchRepresentatives(std::vector<size_t> &chosen)
0830 {
0831 if (budgetHit_ || reps_.size() >= maxReps_)
0832 {
0833 return;
0834 }
0835 if (++nodes_ > budget_)
0836 {
0837 budgetHit_ = true;
0838 return;
0839 }
0840
0841 std::vector<size_t> folded;
0842 FoldIsolated(chosen, folded);
0843
0844 if (CountActive() == 0)
0845 {
0846 if (chosen.size() == target_)
0847 {
0848 Record(chosen);
0849 }
0850 Unfold(chosen, folded);
0851 return;
0852 }
0853
0854 const size_t cover = CliqueCoverBound();
0855 if (chosen.size() + cover < target_)
0856 {
0857 Unfold(chosen, folded);
0858 return;
0859 }
0860 if (cover == 1)
0861 {
0862 for (size_t v = 0; v < prob_.m && reps_.size() < maxReps_; ++v)
0863 {
0864 if (!active_[v])
0865 {
0866 continue;
0867 }
0868 chosen.push_back(v);
0869 Record(chosen);
0870 chosen.pop_back();
0871 }
0872 Unfold(chosen, folded);
0873 return;
0874 }
0875
0876 const size_t vertex = ChooseBranchVertex();
0877
0878 std::vector<size_t> deactivated;
0879 Deactivate(vertex, deactivated);
0880 chosen.push_back(vertex);
0881 SearchRepresentatives(chosen);
0882 chosen.pop_back();
0883 Reactivate(deactivated);
0884
0885 if (reps_.size() < maxReps_)
0886 {
0887 active_[vertex] = false;
0888 SearchRepresentatives(chosen);
0889 active_[vertex] = true;
0890 }
0891
0892 Unfold(chosen, folded);
0893 }
0894
0895 const ComponentProblem &prob_;
0896 std::vector<bool> active_;
0897 std::vector<size_t> scratchKeyCount_;
0898 std::vector<bool> covered_;
0899 size_t bestCardinality_ = 0;
0900 std::vector<size_t> bestSet_;
0901 size_t target_ = 0;
0902 size_t maxReps_ = 1;
0903 std::vector<std::vector<size_t>> reps_;
0904 size_t budget_ = 0;
0905 size_t nodes_ = 0;
0906 bool budgetHit_ = false;
0907 bool debugDetailed_ = false;
0908 size_t debugComponentIndex_ = 0;
0909 size_t debugDepth_ = 0;
0910 };
0911
0912 std::vector<size_t> SelectLargestMvtxDisjointSubset(
0913 const std::vector<const SeedRecord *> &completeSeeds,
0914 gsl_rng *rng,
0915 size_t maxRepresentatives,
0916 size_t searchBudget,
0917 size_t heuristicRestarts,
0918 size_t debugMinimumGroupSize,
0919 std::vector<size_t> &uncertifiedOut,
0920 size_t ambiguityGroupSize,
0921 bool debugVerbosity,
0922 bool debugDetailedVerbosity)
0923 {
0924 if (completeSeeds.empty())
0925 {
0926 return {};
0927 }
0928
0929 const bool debug = (debugVerbosity || debugDetailedVerbosity) && ambiguityGroupSize > debugMinimumGroupSize;
0930
0931 const std::vector<TripletClass> classes = CollapseDuplicateTriplets(completeSeeds, debugVerbosity);
0932 const ConflictGraph classConflict = BuildClassConflictGraph(classes);
0933 if (debug)
0934 {
0935 std::cout << __func__ << " : " << __LINE__ << " : collapsed " << completeSeeds.size() << " complete MVTX seeds into " << classes.size() << " unique MVTX-triplet classes" << std::endl;
0936 }
0937
0938 std::vector<size_t> selectedSeedIndices;
0939
0940 const std::vector<std::vector<size_t>> components = ConnectedComponents(classConflict);
0941 if (debug)
0942 {
0943 std::cout << __func__ << " : " << __LINE__ << " : class conflict graph split into " << components.size() << " connected component(s)" << std::endl;
0944 }
0945
0946 size_t componentIndex = 0;
0947 for (const std::vector<size_t> &component : components)
0948 {
0949 const ComponentProblem prob = BuildComponentProblem(classes, classConflict, component);
0950 ComponentSolver solver(prob, debugDetailedVerbosity && ambiguityGroupSize > debugMinimumGroupSize, componentIndex);
0951
0952 if (debug)
0953 {
0954 std::cout << __func__ << " : " << __LINE__ << " : component " << componentIndex << " has " << prob.m << " class(es) and " << prob.numKeys << " MVTX key(s)" << std::endl;
0955 }
0956
0957 const bool certified = solver.SolveBudgeted(searchBudget);
0958 if (debug)
0959 {
0960 std::cout << __func__ << " : " << __LINE__ << " : component " << componentIndex << " exact search " << (certified ? "closed" : "hit the state budget")
0961 << "; best independent-set size = " << solver.BestCardinality() << std::endl;
0962 }
0963
0964 std::vector<size_t> chosenLocal;
0965 if (certified)
0966 {
0967 const size_t target = solver.BestCardinality();
0968 const std::vector<std::vector<size_t>> reps = solver.CollectRepresentatives(target, maxRepresentatives, searchBudget);
0969 if (debug)
0970 {
0971 std::cout << __func__ << " : " << __LINE__ << " : component " << componentIndex << " collected " << reps.size() << " representative maximum set(s) for target size " << target
0972 << std::endl;
0973 }
0974
0975 if (reps.empty())
0976 {
0977 chosenLocal = solver.BestSet();
0978 }
0979 else
0980 {
0981 size_t chosenRep = 0;
0982 if (reps.size() > 1)
0983 {
0984 std::vector<double> weights;
0985 weights.reserve(reps.size());
0986 for (const std::vector<size_t> &rep : reps)
0987 {
0988 double weight = 1.0;
0989 for (const size_t local : rep)
0990 {
0991 weight *= prob.memberCount[local];
0992 }
0993 weights.push_back(weight);
0994 }
0995 gsl_ran_discrete_t *repPick = gsl_ran_discrete_preproc(weights.size(), weights.data());
0996 chosenRep = gsl_ran_discrete(rng, repPick);
0997 gsl_ran_discrete_free(repPick);
0998 }
0999 chosenLocal = reps[chosenRep];
1000 }
1001 }
1002 else
1003 {
1004 chosenLocal = solver.BuildHeuristicSet(heuristicRestarts, rng);
1005 if (debug)
1006 {
1007 std::cout << __func__ << " : " << __LINE__ << " : component " << componentIndex << " heuristic polished set size = " << chosenLocal.size() << std::endl;
1008 }
1009 }
1010
1011 for (const size_t local : chosenLocal)
1012 {
1013 const std::vector<size_t> &members = classes[prob.globalClassId[local]].memberSeedIndices;
1014 const size_t seedIndex = members[gsl_rng_uniform_int(rng, members.size())];
1015 selectedSeedIndices.push_back(seedIndex);
1016 if (!certified)
1017 {
1018 uncertifiedOut.push_back(seedIndex);
1019 }
1020 }
1021 if (debug)
1022 {
1023 std::cout << __func__ << " : " << __LINE__ << " : component " << componentIndex << " selected " << chosenLocal.size() << " seed(s)" << std::endl;
1024 }
1025 ++componentIndex;
1026 }
1027
1028 return selectedSeedIndices;
1029 }
1030
1031 std::vector<std::vector<size_t>> BuildAmbiguityGroups(const std::vector<SeedRecord> &seeds)
1032 {
1033 DisjointSet disjoint_set(seeds.size());
1034 std::unordered_map<TrkrDefs::cluskey, size_t> first_seed_by_cluster_key;
1035
1036 for (size_t iseed = 0; iseed < seeds.size(); ++iseed)
1037 {
1038 for (const TrkrDefs::cluskey key : seeds[iseed].allClusterKeys)
1039 {
1040 const auto inserted = first_seed_by_cluster_key.emplace(key, iseed);
1041 if (!inserted.second)
1042 {
1043 disjoint_set.Union(iseed, inserted.first->second);
1044 }
1045 }
1046 }
1047
1048 std::unordered_map<size_t, std::vector<size_t>> groups_by_root;
1049 for (size_t iseed = 0; iseed < seeds.size(); ++iseed)
1050 {
1051 groups_by_root[disjoint_set.Find(iseed)].push_back(iseed);
1052 }
1053
1054 std::vector<std::vector<size_t>> groups;
1055 groups.reserve(groups_by_root.size());
1056 for (auto &entry : groups_by_root)
1057 {
1058 groups.push_back(entry.second);
1059 }
1060
1061 return groups;
1062 }
1063
1064
1065 std::vector<size_t> SelectRandomSeedFromGroup(
1066 const std::vector<SeedRecord> &seeds,
1067 const std::vector<size_t> &group,
1068 gsl_rng *rng
1069 )
1070 {
1071 const size_t selected = gsl_rng_uniform_int(rng, group.size());
1072 return {seeds[group[selected]].index};
1073 }
1074
1075 std::vector<size_t> SelectAmbiguityGroup(
1076 const std::vector<SeedRecord> &seeds,
1077 const std::vector<size_t> &group,
1078 gsl_rng *rng,
1079 size_t maxRepresentatives,
1080 size_t searchBudget,
1081 size_t heuristicRestarts,
1082 size_t debugMinimumGroupSize,
1083 std::vector<size_t> &uncertifiedOut,
1084 bool debugVerbosity,
1085 bool debugDetailedVerbosity)
1086 {
1087 const bool debug = (debugVerbosity || debugDetailedVerbosity) && group.size() > debugMinimumGroupSize;
1088
1089 if (debug)
1090 {
1091 std::cout << __func__ << " : " << __LINE__ << " : ambiguity group size = " << group.size() << std::endl;
1092 }
1093
1094 if (group.size() == 1)
1095 {
1096 return {seeds[group.front()].index};
1097 }
1098
1099 std::vector<const SeedRecord *> complete_seeds;
1100 complete_seeds.reserve(group.size());
1101
1102 bool all_complete = true;
1103 for (const size_t local_seed_index : group)
1104 {
1105 const SeedRecord &seed = seeds[local_seed_index];
1106 if (seed.completeMvtx)
1107 {
1108 complete_seeds.push_back(&seed);
1109 }
1110 else
1111 {
1112 all_complete = false;
1113 }
1114 }
1115
1116 if (debug)
1117 {
1118 std::cout << __func__ << " : " << __LINE__ << " : complete MVTX seeds in group = " << complete_seeds.size() << "/" << group.size() << std::endl;
1119
1120 if (debugDetailedVerbosity)
1121 {
1122 for (const size_t local_seed_index : group)
1123 {
1124 const SeedRecord &seed = seeds[local_seed_index];
1125 std::cout << __func__ << " : " << __LINE__ << " : seed index " << seed.index << " completeMvtx=" << seed.completeMvtx << " cluster keys: ";
1126 for (const TrkrDefs::cluskey key : seed.allClusterKeys)
1127 {
1128 std::cout << key << " ";
1129 }
1130 std::cout << std::endl;
1131 }
1132 }
1133 }
1134
1135 if (all_complete)
1136 {
1137 bool all_same_triplet = true;
1138 const SeedRecord &first_seed = seeds[group.front()];
1139 for (const size_t local_seed_index : group)
1140 {
1141 if (first_seed.mvtxClusterKeys != seeds[local_seed_index].mvtxClusterKeys)
1142 {
1143 all_same_triplet = false;
1144 break;
1145 }
1146 }
1147
1148 if (all_same_triplet)
1149 {
1150 if (debug)
1151 {
1152 std::cout << __func__ << " : " << __LINE__ << " : all complete seeds share one MVTX triplet; selecting one random representative" << std::endl;
1153 }
1154 return SelectRandomSeedFromGroup(seeds, group, rng);
1155 }
1156
1157 if (debug)
1158 {
1159 std::cout << __func__ << " : " << __LINE__ << " : all complete seeds have distinct MVTX triplets; selecting largest disjoint subset" << std::endl;
1160 }
1161 return SelectLargestMvtxDisjointSubset(complete_seeds, rng, maxRepresentatives, searchBudget, heuristicRestarts, debugMinimumGroupSize, uncertifiedOut, group.size(), debugVerbosity, debugDetailedVerbosity);
1162 }
1163
1164 if (complete_seeds.empty())
1165 {
1166 if (debug)
1167 {
1168 std::cout << __func__ << " : " << __LINE__ << " : no complete MVTX seeds in group; selecting one random representative" << std::endl;
1169 }
1170 return SelectRandomSeedFromGroup(seeds, group, rng);
1171 }
1172
1173 return SelectLargestMvtxDisjointSubset(complete_seeds, rng, maxRepresentatives, searchBudget, heuristicRestarts, debugMinimumGroupSize, uncertifiedOut, group.size(), debugVerbosity, debugDetailedVerbosity);
1174 }
1175
1176 Result SelectPrunedSiliconSeeds(
1177 const std::vector<SeedRecord> &seeds,
1178 gsl_rng *rng,
1179 size_t maxRepresentatives,
1180 size_t searchBudget,
1181 size_t heuristicRestarts,
1182 size_t debugMinimumGroupSize,
1183 bool debugVerbosity,
1184 bool debugDetailedVerbosity)
1185 {
1186 Result result;
1187 if (seeds.empty())
1188 {
1189 return result;
1190 }
1191
1192 const std::vector<std::vector<size_t>> groups = BuildAmbiguityGroups(seeds);
1193 for (const auto &group : groups)
1194 {
1195 const std::vector<size_t> selected_group_indices =
1196 SelectAmbiguityGroup(seeds, group, rng, maxRepresentatives, searchBudget, heuristicRestarts, debugMinimumGroupSize, result.uncertifiedSeedIndices, debugVerbosity, debugDetailedVerbosity);
1197 result.selectedSeedIndices.insert(
1198 result.selectedSeedIndices.end(),
1199 selected_group_indices.begin(),
1200 selected_group_indices.end());
1201 }
1202
1203 std::sort(result.selectedSeedIndices.begin(), result.selectedSeedIndices.end());
1204 std::sort(result.uncertifiedSeedIndices.begin(), result.uncertifiedSeedIndices.end());
1205 return result;
1206 }
1207
1208 Result SelectSeeds(
1209 const TrackSeedContainer &container,
1210 const std::vector<std::size_t> &seedIndices,
1211 gsl_rng *rng,
1212 std::size_t mvtxLayerCount,
1213 std::size_t maxRepresentatives,
1214 std::size_t searchBudget,
1215 std::size_t heuristicRestarts,
1216 std::size_t debugMinimumGroupSize,
1217 bool debugVerbosity,
1218 bool debugDetailedVerbosity)
1219 {
1220 std::vector<SeedRecord> seeds;
1221 seeds.reserve(seedIndices.size());
1222 for (const std::size_t seedIndex : seedIndices)
1223 {
1224 seeds.push_back(MakeSeedRecord(seedIndex, *container.get(seedIndex), mvtxLayerCount));
1225 }
1226
1227 return SelectPrunedSiliconSeeds(
1228 seeds,
1229 rng,
1230 maxRepresentatives,
1231 searchBudget,
1232 heuristicRestarts,
1233 debugMinimumGroupSize,
1234 debugVerbosity,
1235 debugDetailedVerbosity);
1236 }
1237 }