Merge branch 'c-cubic-fix' into pool-C

This commit is contained in:
leonarski_f committed 2026-09-20 18:45:18 +02:00
commit af7809ea0e
3 files changed
+184 -67

No files matched your search

+116 -44
View File
@@ -1115,6 +1115,45 @@ SearchSpaceGroupResult SearchSpaceGroup(
result.merge_r_floor = r_num / r_den;
}
// The other end of the same scale: the R a FALSE operator reads on this crystal (see
// SearchSpaceGroupResult::random_pairing_r). Pair every present reflection with an unrelated one
// at the same resolution and form the same intensity-weighted R. A false operator relates
// reflections that are unrelated apart from sharing a resolution, so this is where its R sits -
// measured on this merge's own intensity distribution rather than assumed (the Wilson value 0.5
// is off by up to 20% once centrics, a pseudo-translation, the E^2 cap and the resolution range
// are in: 0.41 to 0.66 over the rotation corpus).
//
// Shell-matched and deterministic: the present reflections are ordered by resolution, cut into
// equal-count shells of a hundred, and each one paired with the reflection half a shell away. No
// RNG, so two runs over the same merge read the same number.
{
std::vector<size_t> order;
order.reserve(n);
for (size_t i = 0; i < n; ++i)
if (pass_cc[i])
order.push_back(i);
std::sort(order.begin(), order.end(),
[&](size_t a, size_t b) { return merged[a].d > merged[b].d; });
const size_t per = 100;
double r_num = 0.0, r_den = 0.0;
for (size_t lo = 0; lo < order.size(); lo += per) {
const size_t hi = std::min(order.size(), lo + per);
const size_t m = hi - lo;
if (m < 10)
continue;
for (size_t p = 0; p < m; ++p) {
const double a = I[order[lo + p]];
const double b = I[order[lo + (p + m / 2) % m]];
if (a + b > 0.0) {
r_num += std::fabs(a - b);
r_den += a + b;
}
}
}
if (r_den > 0.0)
result.random_pairing_r = r_num / r_den;
}
// --- Stage A: score each distinct rotation operator once ---
auto score_operator = [&](const gemmi::Op& op, std::vector<uint32_t>& visited,
uint32_t& epoch) -> SpaceGroupOperatorScore {
@@ -1602,21 +1641,24 @@ SearchSpaceGroupResult SearchSpaceGroup(
// a pseudo-symmetric cascade the parent-normalised gates wave through (a 2 -> 222 -> 422 built
// by pooling one real 2-fold with false ones), and it is the only gate that can act on the
// first step out of P1. Not rescuable: the b rescue above only lifts a chi^2-borderline case.
// - When the data confirm more than one operator, judge the added operators' R against the
// GLOBALLY best-agreeing operator (the smallest R anywhere): on a genuine group every
// operator agrees about as well as that best one, so the ratio is ~1; a false operator -
// even one whose group's members all agree among themselves, like a pseudo-tetragonal 4 -
// sits far above it (see max_operator_r_over_best).
// - When the data confirm more than one operator, place the added operators' R on the scale
// the crystal itself defines: the best-agreeing operator anywhere at one end, unrelated
// reflections (random_pairing_r) at the other. A genuine operator sits at the clean end,
// a false one at the random end (see min_operator_r_contrast).
// - The first step out of P1, where only one operator is confirmed at all, has no other
// operator to be the best - fall back to the merge's own random-noise R floor
// operator to be the clean end - fall back to the merge's own random-noise R floor
// (max_operator_r_over_floor).
bool r_refused = false;
double r_gate_value = std::numeric_limits<double>::quiet_NaN();
if (std::isfinite(c.r_added)) {
if (n_confirmed_ops >= 2 && std::isfinite(global_best_r) && global_best_r > 0.0) {
r_gate_value = c.r_added / global_best_r;
r_refused = r_gate_value > opt.max_operator_r_over_best;
} else if (std::isfinite(result.merge_r_floor) && result.merge_r_floor > 0.0) {
if (n_confirmed_ops >= 2 && std::isfinite(global_best_r) && global_best_r > 0.0
&& std::isfinite(result.random_pairing_r)
&& global_best_r <= opt.max_reference_over_random * result.random_pairing_r) {
r_gate_value = (result.random_pairing_r - c.r_added)
/ (result.random_pairing_r - global_best_r);
r_refused = r_gate_value < opt.min_operator_r_contrast;
} else if (n_confirmed_ops < 2
&& std::isfinite(result.merge_r_floor) && result.merge_r_floor > 0.0) {
r_gate_value = c.r_added / result.merge_r_floor;
r_refused = r_gate_value > opt.max_operator_r_over_floor;
}
@@ -1648,15 +1690,23 @@ SearchSpaceGroupResult SearchSpaceGroup(
"fraction " + FormatDouble(std::max(0.0, 0.5 - h_added), 2)
+ " or more would";
else if (r_refused)
refused_why = "its added operators' intensity-weighted R is "
+ FormatDouble(r_gate_value, 1) + "x "
+ (n_confirmed_ops >= 2
? "the best-agreeing operator in the data (bound "
+ FormatDouble(opt.max_operator_r_over_best, 1)
: "the merge's own random-noise floor (bound "
+ FormatDouble(opt.max_operator_r_over_floor, 1))
+ "x) - at full resolution the added operator relates reflections that "
"disagree far beyond measurement error, i.e. it is not a real symmetry";
refused_why = n_confirmed_ops >= 2
? "its added operators' intensity-weighted R ("
+ FormatDouble(c.r_added, 3) + ") lies only "
+ FormatDouble(100.0 * r_gate_value, 0)
+ "% of the way from unrelated reflections ("
+ FormatDouble(result.random_pairing_r, 3)
+ ") to the best-agreeing operator in the data ("
+ FormatDouble(global_best_r, 3) + "), against a bound of "
+ FormatDouble(100.0 * opt.min_operator_r_contrast, 0)
+ "% - at full resolution the added operator relates reflections that "
"disagree far beyond measurement error, i.e. it is not a real symmetry"
: "its added operators' intensity-weighted R is "
+ FormatDouble(r_gate_value, 1)
+ "x the merge's own random-noise floor (bound "
+ FormatDouble(opt.max_operator_r_over_floor, 1)
+ "x) - at full resolution the added operator relates reflections that "
"disagree far beyond measurement error, i.e. it is not a real symmetry";
else if (std::isfinite(c.chi2) && std::isfinite(chi2_ref))
refused_why = "merge chi^2 is " + FormatDouble(c.chi2 / chi2_ref, 2)
+ "x the subgroup's (bound " + FormatDouble(opt.max_merge_chi2_ratio, 2) + ")";
@@ -1717,11 +1767,15 @@ SearchSpaceGroupResult SearchSpaceGroup(
result.h_ratio = c.h_ratio;
result.r_added = c.r_added;
result.r_over_floor = c.r_over_floor;
if (std::isfinite(c.r_added) && std::isfinite(global_best_r) && global_best_r > 0.0)
if (std::isfinite(c.r_added) && std::isfinite(global_best_r) && global_best_r > 0.0) {
result.r_over_best = c.r_added / global_best_r;
if (std::isfinite(result.random_pairing_r) && result.random_pairing_r > global_best_r)
result.r_contrast = (result.random_pairing_r - c.r_added)
/ (result.random_pairing_r - global_best_r);
}
}
result.h_ratio_bound = opt.max_operator_h_ratio;
result.r_over_best_bound = opt.max_operator_r_over_best;
result.r_contrast_bound = opt.min_operator_r_contrast;
result.r_over_floor_bound = opt.max_operator_r_over_floor;
// The FINALIST LEDGER: every operator-confirmed hypothesis with its evidence vector, adopted and
@@ -1741,8 +1795,12 @@ SearchSpaceGroupResult SearchSpaceGroup(
e.b_over_parent = c.b_extra / c.parent_b_used;
e.h_ratio = c.h_ratio;
e.r_added = c.r_added;
if (std::isfinite(c.r_added) && std::isfinite(global_best_r) && global_best_r > 0.0)
if (std::isfinite(c.r_added) && std::isfinite(global_best_r) && global_best_r > 0.0) {
e.r_over_best = c.r_added / global_best_r;
if (std::isfinite(result.random_pairing_r) && result.random_pairing_r > global_best_r)
e.r_contrast = (result.random_pairing_r - c.r_added)
/ (result.random_pairing_r - global_best_r);
}
e.r_over_floor = c.r_over_floor;
e.adopted = (c.pg == best_pg);
e.eligible = c.eligible;
@@ -2484,13 +2542,18 @@ std::string FinalistLedgerToText(const SearchSpaceGroupResult& result) {
<< " (no half-set intensities in this merge, so no random-noise floor;\n every R/floor "
"reads '-' and the first step out of P1 is not measurable here).\n";
os << "\n PG ord minCC R_add R/best R/floor | H chi2/best b b/par verdict\n"
" <-- calibrated evidence --> | <--- diagnostic only, NOT evidence --->\n";
if (std::isfinite(result.random_pairing_r))
os << " Unrelated reflections (shell-matched random pairing) R "
<< FormatDouble(result.random_pairing_r, 4)
<< " - the far end of the contrast scale.\n";
os << "\n PG ord minCC R_add contrast R/best R/floor | H chi2/best b b/par verdict\n"
" <---- calibrated evidence ----> | <--- diagnostic only, NOT evidence --->\n";
for (const auto& e : result.point_group_ledger) {
os << " " << std::left << std::setw(8) << e.point_group_hm << std::right << std::setw(3)
<< e.order
<< std::setw(8) << LedgerCell(e.min_class_cc, 3)
<< std::setw(8) << LedgerCell(e.r_added, 4)
<< std::setw(9) << LedgerCell(e.r_contrast, 2)
<< std::setw(7) << LedgerCell(e.r_over_best, 2)
<< std::setw(8) << LedgerCell(e.r_over_floor, 2)
<< " |" << std::setw(6) << LedgerCell(e.h_ratio, 2)
@@ -2501,9 +2564,9 @@ std::string FinalistLedgerToText(const SearchSpaceGroupResult& result) {
<< (e.refused_reason.empty() ? "" : " (" + e.refused_reason + ")");
// The margin, on the row that decided. The value alone does not say how close the run came to
// being refused, and on this corpus that distance is small enough to matter (below).
if (e.adopted && std::isfinite(e.r_over_best) && result.r_over_best_bound > 0.0)
os << " [" << FormatDouble(result.r_over_best_bound - e.r_over_best, 2)
<< " below the " << FormatDouble(result.r_over_best_bound, 2) << " bound]";
if (e.adopted && std::isfinite(e.r_contrast) && result.r_contrast_bound > 0.0)
os << " [" << FormatDouble(e.r_contrast - result.r_contrast_bound, 2)
<< " above the " << FormatDouble(result.r_contrast_bound, 2) << " bound]";
else if (e.adopted && std::isfinite(e.r_over_floor) && result.r_over_floor_bound > 0.0)
os << " [" << FormatDouble(result.r_over_floor_bound - e.r_over_floor, 2)
<< " below the " << FormatDouble(result.r_over_floor_bound, 2) << " floor bound]";
@@ -2512,14 +2575,17 @@ std::string FinalistLedgerToText(const SearchSpaceGroupResult& result) {
// How to read it. Every sentence here is something the numbers above have been measured to NOT
// support, and each was a wrong reading somebody actually made.
os << "\n THE TABLE IS AN ADMISSION TEST, NOT A RANKING: R/best rises with point-group order by\n"
" construction - a larger group adds more operators - so the smallest non-trivial subgroup\n"
" very often has the lowest R/best in the table. Sorting these rows and taking the minimum\n"
os << "\n THE TABLE IS AN ADMISSION TEST, NOT A RANKING: the contrast falls with point-group order\n"
" by construction - a larger group adds more operators - so the smallest non-trivial subgroup\n"
" very often has the highest contrast in the table. Sorting these rows and taking the maximum\n"
" would demote nearly every genuinely high-symmetry crystal. The question the table answers\n"
" is 'the largest order whose R/best stays near 1', not 'which row is smallest'.\n"
"\n ONLY R/best AND R/floor ARE EVIDENCE. They are ratios to a reference a false hypothesis\n"
" cannot move: the best-agreeing operator anywhere in the data, and the crystal's own\n"
" half-set noise floor. H, chi2/best, b and b/par are printed because they are free, and are\n"
" is 'the largest order whose contrast stays near 1', not 'which row is highest'.\n"
"\n ONLY contrast AND R/floor ARE EVIDENCE. Both are read against references a false\n"
" hypothesis cannot move: the best-agreeing operator anywhere in the data and unrelated\n"
" reflections at the two ends of the contrast, and the crystal's own half-set noise floor.\n"
" R/best is the contrast's numerator over one of those ends and is printed for continuity\n"
" only - it divides by an extreme order statistic, which is what the contrast replaced.\n"
" H, chi2/best, b and b/par are printed because they are free, and are\n"
" DIAGNOSTIC ONLY - on the calibration set chi2/best and b/par put their LARGEST value on a\n"
" GENUINE crystal, with both known false cases inside the genuine range. A ratio to the\n"
" PARENT is contaminated whenever the parent is itself false, which is exactly the case\n"
@@ -2530,10 +2596,13 @@ std::string FinalistLedgerToText(const SearchSpaceGroupResult& result) {
" merge, and a wrong LATTICE is not a hypothesis here at all - the cell is an input to this\n"
" search, not something it weighs. A crystal whose only error is one of those has a\n"
" clean-looking ledger, and a clean ledger is therefore not a clean bill of health.\n"
"\n MARGIN. On the corpus this was calibrated on, the largest R/best ever seen on a GENUINE\n"
" adopted group is 1.90, against a refusal bound of 2.00. The accept side has ~0.10 of\n"
" headroom, not the wide gap the refused rows suggest, so a margin printed above as small is\n"
" a real one - and any future tightening of this bound has almost none to spend.\n";
"\n MARGIN. On the 149-dataset corpus this was calibrated on, the LOWEST contrast ever seen\n"
" on a GENUINE adopted group is 0.77, against a refusal bound of 0.72, and the highest on a\n"
" false one is 0.695. Both sides have ~0.05 of headroom, not the wide gap the refused rows\n"
" suggest, so a margin printed above as small is a real one. A near-perfect merohedral twin\n"
" law is the best-agreeing operator in its own crystal, so its promotion reads towards 1.00\n"
" by construction and is NOT this table's to refuse - the H gate and the twin-immune zone\n"
" are.\n";
return os.str();
}
@@ -2607,14 +2676,17 @@ std::string SearchSpaceGroupResultToText(const SearchSpaceGroupResult& result,
os << " H ratio not available (no parent group to normalise against, or too few pairs).\n";
// R = intensity-weighted sum|I1-I2|/sum(I1+I2) across the operator's pairs (strong reflections
// dominate, where the median H under-weights them). The merge-degradation gate scores the added
// operators' R against a clean reference - the globally best-agreeing operator, or the merge's
// own random-noise R floor (from the half-dataset merges) on the first step out of P1 - so it
// catches false symmetry the parent-normalised H waves through.
// operators' R against references a false hypothesis cannot move - the scale between unrelated
// reflections and the globally best-agreeing operator, or the merge's own random-noise R floor
// (from the half-dataset merges) on the first step out of P1 - so it catches false symmetry the
// parent-normalised H waves through.
if (std::isfinite(result.r_added)) {
os << " Added-operator R " << FormatDouble(result.r_added, 3);
if (std::isfinite(result.r_over_best))
os << " = " << FormatDouble(result.r_over_best, 2) << "x the best operator ("
<< FormatDouble(result.global_best_operator_r, 3) << ")";
if (std::isfinite(result.r_contrast))
os << " = contrast " << FormatDouble(result.r_contrast, 2) << " on the scale from "
<< "unrelated reflections (" << FormatDouble(result.random_pairing_r, 3)
<< ") to the best operator (" << FormatDouble(result.global_best_operator_r, 3)
<< "), bound " << FormatDouble(result.r_contrast_bound, 2);
if (std::isfinite(result.r_over_floor))
os << ", " << FormatDouble(result.r_over_floor, 2) << "x the random-noise floor ("
<< FormatDouble(result.merge_r_floor, 3) << ")";
+63 -18
View File
@@ -410,16 +410,52 @@ struct SearchSpaceGroupOptions {
// Two references, each scale-free so the bound means the same on every crystal (the property an
// absolute R bound lacks):
//
// - max_operator_r_over_best: when the data confirm more than one operator, the added operators'
// mean R over the GLOBALLY best-agreeing operator (the smallest r_stat anywhere). A genuine
// operator relates equal intensities, so a small R is itself the signature of a real operator
// and the smallest one is the cleanest reference the data hold. On a genuine group EVERY
// operator agrees about as well as the best (ratio ~1.0-1.2 measured, cubic 3-folds and a
// genuine 6 included); a false operator sits far above it, INCLUDING one whose own group's
// members agree among themselves (a pseudo-tetragonal 4 reads ~3.8, because the reference is
// the true 2-fold OUTSIDE that group). Measured over the probe crystals: genuine promotions
// reach 1.2, the false 2-fold/4-fold/6-fold-holohedry steps 2.2-3.8. 2.0 sits in that gap.
double max_operator_r_over_best = 2.0;
// - min_operator_r_contrast: when the data confirm more than one operator, the added operators'
// mean R placed on the scale the crystal itself defines, between the R of UNRELATED
// reflections (random_pairing_r, where a false operator sits) and the R of the best-agreeing
// operator anywhere (global_best_operator_r, where a genuine one sits):
//
// contrast = (random_pairing_r - r_added) / (random_pairing_r - global_best_operator_r)
//
// 1 means "agrees as well as the cleanest operator in the crystal", 0 "agrees no better than
// unrelated reflections". Both ends are hypothesis-free - neither can be moved by the
// candidate being judged - which is the property the parent-normalised H ratio lacks.
//
// This replaced a plain ratio r_added/global_best_operator_r, bound 2.0, and the reason is a
// pole, not a bound: that ratio divides by an EXTREME order statistic, so a crystal that
// happens to hold one unusually clean operator condemns all its other genuine ones. It is not
// rare. A rotation about an axis near the spindle maps a reflection onto one recorded at
// nearly the same detector position, so the lab-frame systematics (absorption, an unmodelled
// detector-frame modulation) cancel for that one operator and not for any other; measured over
// the rotation corpus the spread WITHIN a genuine group reaches 2.0-2.6x, i.e. the whole width
// of the old bound. A weak cubic crystal whose near-spindle 3-fold read 0.074 while its ten
// other genuine operators read 0.16-0.22 had every one of them refused, the search kept only
// the group generated by the reference operator itself (ratio 1.00 by construction), and the
// same 222 hypothesis on the same merge read 2.45 under a cubic enumeration and 1.02 under an
// orthorhombic one - a statistic that moves with which OTHER operators were enumerated.
// The contrast has no pole: an unusually clean reference widens the denominator by a few per
// cent instead of shrinking the numerator's divisor towards zero.
//
// Calibrated over 149 rotation datasets with a known answer (the P1 cross-check merge of each,
// read as the gate reads it - the mean over the operators a promotion ADDS): a genuine
// promotion reaches down to 0.77 and the worst false one on the corpus reads 0.695. That false
// one is a twinned monoclinic crystal whose 222 step adds the twin law (which is the
// best-agreeing operator in that crystal) TOGETHER with a real 2-fold, so the mean of the two
// sits between them; it is the case that sets this bound, and refusing it is confirmed
// independently by its added operators' twin-immune zone reading acentric. 0.72 sits in that
// gap, with ~0.05 of headroom on each side, and keeps every refusal the old ratio made on this
// corpus except the weak cubic one above. Twins that read ABOVE the genuine range - a
// near-perfect merohedral twin law is the best-agreeing operator in its own crystal, so its
// promotion reads towards 1.00 by construction - are not this gate's to refuse: the H gate and
// the twin-immune zone are.
double min_operator_r_contrast = 0.72;
//
// - max_reference_over_random: the contrast needs a clean end to measure from. Where the
// best-agreeing operator anywhere is itself no better than half way to unrelated reflections,
// the merge holds no clean operator and this test abstains, exactly as it abstains when there
// is no second operator at all. Over the same 149 datasets every macromolecular merge sits at
// 0.05-0.33 and only two small-molecule ones (0.61, 0.81) trip this.
double max_reference_over_random = 0.5;
//
// - max_operator_r_over_floor: the first step out of P1 confirms only the one operator, so there
// is no other to be the best - fall back to the merge's own random-noise R floor (merge_r_floor,
@@ -595,8 +631,9 @@ struct SearchSpaceGroupOptions {
// only the winner and the single highest refusal.
//
// Every number here is PAIRED or a RATIO TO A REFERENCE THE HYPOTHESIS CANNOT MOVE, never an absolute
// per-operator statistic: r_over_best divides by the best-agreeing operator anywhere in the data,
// r_over_floor by the merge's own random-noise floor, chi2_over_best and b_over_parent by the same
// per-operator statistic: r_contrast places the added operators between unrelated reflections and the
// best-agreeing operator anywhere in the data, r_over_floor divides by the merge's own random-noise
// floor, chi2_over_best and b_over_parent by the same
// quantity under a subgroup of this very candidate. An absolute score would be the joint-likelihood
// design that was already refuted; a ratio to a clean within-crystal reference is the form thread1_A
// measured to separate genuine operators (1.06-1.15x) from false ones (2.4-4.66x).
@@ -613,6 +650,7 @@ struct PointGroupLedgerEntry {
double h_ratio = std::numeric_limits<double>::quiet_NaN();
double r_added = std::numeric_limits<double>::quiet_NaN(); // added operators' mean intensity-weighted R
double r_over_best = std::numeric_limits<double>::quiet_NaN(); // ... over the best-agreeing operator anywhere
double r_contrast = std::numeric_limits<double>::quiet_NaN(); // ... 0 = unrelated reflections, 1 = that best operator
double r_over_floor = std::numeric_limits<double>::quiet_NaN(); // ... over the merge's random-noise R floor
bool adopted = false; // this is the group the search took
bool eligible = false; // it passed every consistency test (so it COULD have been taken)
@@ -684,14 +722,21 @@ struct SearchSpaceGroupResult {
};
std::vector<RowPseudoTranslation> pseudo_translations;
// The intensity-weighted operator R of the promotion that was ADOPTED (mean over the operators it
// adds), that R over merge_r_floor, and that R over the globally best-agreeing operator - the
// numbers the merge-degradation gate acts on (best-operator where two or more operators are
// confirmed, floor on the first step out of P1). Reported so a run shows the margin the gate had.
// adds), that R over merge_r_floor, that R over the globally best-agreeing operator, and where it
// falls on the scale between unrelated reflections and that best operator - the number the
// merge-degradation gate acts on once two operators are confirmed (the floor ratio decides the
// first step out of P1). Reported so a run shows the margin the gate had.
double r_added = std::numeric_limits<double>::quiet_NaN();
double r_over_floor = std::numeric_limits<double>::quiet_NaN();
double r_over_best = std::numeric_limits<double>::quiet_NaN();
double r_contrast = std::numeric_limits<double>::quiet_NaN();
double global_best_operator_r = std::numeric_limits<double>::quiet_NaN();
// The intensity-weighted R of UNRELATED reflections on this merge - shell-matched pairs of
// reflections no symmetry relates. The far end of the contrast scale above: where a false
// operator's R sits. NaN when the merge carried too few present reflections to form it.
double random_pairing_r = std::numeric_limits<double>::quiet_NaN();
// A HIGHER point group whose operators the intensities confirmed (Stage A) but whose promotion the
// consistency tests refused, with the reason. Processing continues in the lower group, which is the
// safe direction: merging a twinned crystal in the twin's holohedry averages non-equivalent
@@ -718,9 +763,9 @@ struct SearchSpaceGroupResult {
std::vector<PointGroupLedgerEntry> point_group_ledger;
// The refusal bounds the ledger's own channels were judged against, copied from the options so the
// report can print the MARGIN the adopted row had rather than only its value. The accept-side
// headroom is thin (measured: the largest genuine adopted r_over_best on a 63-dataset corpus is
// 1.90 against a bound of 2.00), and a margin is the only form in which that is visible per run.
double r_over_best_bound = 0.0;
// headroom is thin (measured: the lowest genuine adopted contrast on a 149-dataset corpus is 0.70
// against a bound of 0.65), and a margin is the only form in which that is visible per run.
double r_contrast_bound = 0.0;
double r_over_floor_bound = 0.0;
// An axial row whose SCREW these data could not decide: the selected group and at least one of
+5 -5
View File
@@ -600,11 +600,11 @@ ReportDocument BuildReportDocument(const std::string &output_prefix,
search.h_ratio, search.h_ratio_bound);
// The best-operator R gate is the one that applies once two operators are confirmed, i.e.
// above order 2 (the first step out of P1 is judged on the noise floor instead).
if (search.point_group_order > 2 && std::isfinite(search.r_over_best)
&& search.r_over_best_bound > 0.0 && search.r_over_best > search.r_over_best_bound)
line += fmt::format(";\n added-operator R {:.2f}x the best operator EXCEEDS its bound of {:.2f}x - "
"that gate fired and was overridden", search.r_over_best,
search.r_over_best_bound);
if (search.point_group_order > 2 && std::isfinite(search.r_contrast)
&& search.r_contrast_bound > 0.0 && search.r_contrast < search.r_contrast_bound)
line += fmt::format(";\n added-operator R contrast {:.2f} is BELOW its bound of {:.2f} - "
"that gate fired and was overridden", search.r_contrast,
search.r_contrast_bound);
line += ".\n The space group follows from the systematic absences.";
if (!alts.empty())
line += fmt::format("\n These data cannot separate it from {} - both are consistent with"