Space-group search: the added-operator R gate reads a contrast, not a ratio to the best operator

The full-resolution merge-degradation gate divided the added operators' mean
intensity-weighted R by the smallest R anywhere in the crystal. That denominator is an
extreme order statistic, so it has a pole: one unusually clean operator condemns every
other genuine one. It is not a rare accident. 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 cancel for that operator alone; measured over the rotation corpus the spread
WITHIN a genuine group reaches 2.0-2.6x, the whole width of the old bound of 2.0. On a weak
cubic crystal whose near-spindle 3-fold read 0.074 against 0.16-0.22 for its ten other
genuine operators, every one of them was refused, the search kept only the group generated
by the reference operator itself (which reads 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 added operators are now placed on the scale the crystal itself defines, between the R
of unrelated reflections and the R of the best-agreeing operator:

    contrast = (random_pairing_r - r_added) / (random_pairing_r - global_best_operator_r)

random_pairing_r is new: the same intensity-weighted R over shell-matched pairs of
reflections no symmetry relates, measured on the merge (deterministic, no RNG). Both ends
are hypothesis-free, and the form has no pole - an unusually clean reference widens the
denominator by a few per cent instead of driving the divisor towards zero. The test
abstains where the best-agreeing operator is itself no better than half way to unrelated
reflections, i.e. where the merge holds no clean end to measure from, as it already
abstains when there is no second operator at all.

Calibrated over 149 rotation datasets with a known answer, read as the gate reads it:
genuine promotions reach down to 0.77 and the worst false one reads 0.695, so the bound is
0.72. It keeps every refusal the old ratio made on that corpus except the weak cubic one,
which now promotes to its cubic group; the twins that read above the genuine range are
refused by the H gate and the twin-immune zone, as before.

The reported value, the point-group report line, the finalist ledger and the
gate-fired-and-was-overridden note all follow the contrast.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nW6FNRP1bBJJ8pfHiByAT
This commit is contained in:
2026-09-20 18:45:17 +02:00
co-authored by Claude Opus 5
parent 3ca22c07c1
commit c8e98faf31
3 changed files with 184 additions and 67 deletions
+116 -44
View File
@@ -1063,6 +1063,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 {
@@ -1550,21 +1589,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;
}
@@ -1596,15 +1638,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) + ")";
@@ -1665,11 +1715,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
@@ -1689,8 +1743,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;
@@ -2388,13 +2446,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)
@@ -2405,9 +2468,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]";
@@ -2416,14 +2479,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"
@@ -2434,10 +2500,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();
}
@@ -2511,14 +2580,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
@@ -403,16 +403,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,
@@ -584,8 +620,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).
@@ -602,6 +639,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)
@@ -673,14 +711,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
@@ -707,9 +752,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;
// The best SOHNCKE candidate, always, even when a glide plane was confirmed and best_space_group
+5 -5
View File
@@ -564,11 +564,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"