|
2 | 2 | #include <BALL/STRUCTURE/assignBondOrderProcessor.h> |
3 | 3 | #include <BALL/KERNEL/PTE.h> |
4 | 4 |
|
| 5 | +#include <algorithm> // BUG-576: std::lexicographical_compare for the total operator< tie-break |
| 6 | + |
5 | 7 | namespace BALL |
6 | 8 | { |
7 | 9 | #define INFINITE_PENALTY 1e5 |
@@ -33,21 +35,56 @@ namespace BALL |
33 | 35 |
|
34 | 36 | // the less operator |
35 | 37 | // note: we want a reverse sort, hence we actually return a "greater" |
| 38 | + // |
| 39 | + // BUG-576 (#576): This comparator feeds a std::priority_queue (a max-heap that |
| 40 | + // pops the highest-priority element, i.e. the LOWEST penalty, first). For that |
| 41 | + // to be deterministic across platforms the ordering MUST be a TOTAL strict weak |
| 42 | + // ordering: it must distinguish every pair of entries that differ in content, |
| 43 | + // otherwise equivalent entries pop in heap-internal (libc++ vs libstdc++ vs MSVC |
| 44 | + // STL) order and apply(n) returns a different n-th solution per platform. |
| 45 | + // |
| 46 | + // We cascade through content-derived keys, returning as soon as a key differs: |
| 47 | + // (1) coarsePenalty() (the optimisation objective) |
| 48 | + // (2) finePenalty() ALWAYS (not gated on use_fine_penalty_ — that option |
| 49 | + // decides which solutions are optimal, not how the queue |
| 50 | + // must order them; applying it always keeps the FINE |
| 51 | + // subtest passing and breaks coarse ties deterministically) |
| 52 | + // (3) last_bond (Position, numeric) |
| 53 | + // (4) bond_orders (lexicographic, vector<short>) |
| 54 | + // Returning false in both directions is correct ONLY when penalties AND |
| 55 | + // bond_orders are identical, i.e. the entries are genuinely indistinguishable. |
| 56 | + // |
| 57 | + // Direction: operator< returns true when *this should pop AFTER b (i.e. *this is |
| 58 | + // the "greater" entry). At each level we therefore compare *this's key '>' b's key. |
36 | 59 | bool PartialBondOrderAssignment::operator < (const PartialBondOrderAssignment& b) const |
37 | 60 | { |
38 | 61 | bool value = false; |
39 | | - if (coarsePenalty() > b.coarsePenalty()) |
| 62 | + |
| 63 | + float coarse_a = coarsePenalty(); |
| 64 | + float coarse_b = b.coarsePenalty(); |
| 65 | + if (coarse_a != coarse_b) |
40 | 66 | { |
41 | | - value = true; |
| 67 | + value = (coarse_a > coarse_b); |
42 | 68 | } |
43 | 69 | else |
44 | 70 | { |
45 | | - if (coarsePenalty() == b.coarsePenalty()) |
| 71 | + float fine_a = finePenalty(); |
| 72 | + float fine_b = b.finePenalty(); |
| 73 | + if (fine_a != fine_b) |
46 | 74 | { |
47 | | - if (abop->use_fine_penalty_ && (finePenalty() > b.finePenalty())) |
48 | | - { |
49 | | - value = true; |
50 | | - } |
| 75 | + value = (fine_a > fine_b); |
| 76 | + } |
| 77 | + else if (last_bond != b.last_bond) |
| 78 | + { |
| 79 | + value = (last_bond > b.last_bond); |
| 80 | + } |
| 81 | + else |
| 82 | + { |
| 83 | + // total tie-break on the bond-order vector itself. |
| 84 | + // std::lexicographical_compare(b, a) is true iff b < a lexicographically, |
| 85 | + // i.e. iff *this is lexicographically greater -> *this pops after b. |
| 86 | + value = std::lexicographical_compare(b.bond_orders.begin(), b.bond_orders.end(), |
| 87 | + bond_orders.begin(), bond_orders.end()); |
51 | 88 | } |
52 | 89 | } |
53 | 90 |
|
|
0 commit comments