Refinement: the final ladder takes the rungs the step from the best state tried already; the longest trials to the slots first
The trajectory ends where a step gains nothing, and that step ran from the best state at the weights the final ladder then tries from the same state: on a positional step every rung, on a B step the middle one. A trial of the same weights from the same model is the same computation (RunTrials: the outcome is the same on any slot), so the final takes those outcomes from the step and runs only the rungs it has not got. The maps' Refit() then starts from the fit the main slot would hold had it run its rung (Context::Restore of that rung's fit) - the solvent grid and the likelihood's D it starts from otherwise depend on which trial the main slot ran last, and that showed as last-bit differences in the map coefficients. RunTrials gives the trials that refine B, the longest, to the slots first: on a machine whose cards hold a slot each the ladder's two B rungs no longer share one card while a positional rung has one of its own. Byte-identical (p.mtz, p.hkl, p.cif, p_model, p_refined_model, p_refined_maps.mtz, p_blobs.json, report minus timing) on three rotation sets run live-path, and on two of them in --mode refine. Refinement wall on one RTX 5080: 1.86 -> 1.71 s, 2.42 -> 2.17 s, 50.0 -> 40.8 s (one stage of the trajectory each). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015CeEGgpKdfkCTJb3YNMJcu
This commit is contained in:
1 parent
e4d64e67c9
commit
00f8c8ba4b
1 file changed
+41
-8
@@ -9,6 +9,7 @@
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <cmath>
|
||||
#include <numeric>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
@@ -1133,8 +1134,8 @@ TrialOutcome RunTrial(TrialSlot slot, const std::vector<int> &refinable, const s
|
||||
return out;
|
||||
}
|
||||
|
||||
// The trials (w, adp_w) side by side: trial i in slot i % slots, each slot's trials in turn on a thread of its
|
||||
// own (on its GPU), returned in the order given. Every trial starts from pos0 and b0 and takes nothing from the
|
||||
// The trials (w, adp_w) side by side, the longest to the slots first and each slot's trials in turn on a thread of
|
||||
// its own (on its GPU), returned in the order given. Every trial starts from pos0 and b0 and takes nothing from the
|
||||
// one before it in its slot, so the outcome is the same for any number of slots.
|
||||
std::vector<TrialOutcome> RunTrials(const std::vector<TrialSlot> &trial_slots,
|
||||
const std::vector<std::pair<double, double>> &trials,
|
||||
@@ -1147,13 +1148,19 @@ std::vector<TrialOutcome> RunTrials(const std::vector<TrialSlot> &trial_slots,
|
||||
const size_t running = std::min(trial_slots.size(), trials.size());
|
||||
for (size_t s = 0; s < running; s++)
|
||||
trial_slots[s].ctx->nthreads = std::max<size_t>(1, settings.nthreads / running);
|
||||
// The trials that refine B are the longest: they go to the slots first, so that two of them share a slot (or a
|
||||
// card) only where the slots are fewer than they are.
|
||||
std::vector<size_t> order(trials.size());
|
||||
std::iota(order.begin(), order.end(), 0);
|
||||
std::stable_sort(order.begin(), order.end(),
|
||||
[&](size_t a, size_t b) { return (trials[a].second > 0) > (trials[b].second > 0); });
|
||||
auto run_slot = [&](size_t s) {
|
||||
try {
|
||||
#ifdef JFJOCH_USE_CUDA
|
||||
const ScopedGpuPin pin(trial_slots[s].ctx->device >= 0 ? trial_slots[s].ctx->device : get_gpu());
|
||||
#endif
|
||||
for (size_t i = s; i < trials.size(); i += trial_slots.size())
|
||||
if (!trial_slots[s].ctx->Cancelled())
|
||||
for (size_t j = s; j < trials.size(); j += trial_slots.size())
|
||||
if (const size_t i = order[j]; !trial_slots[s].ctx->Cancelled())
|
||||
out[i] = RunTrial(trial_slots[s], refinable, pos0, b0, fit, trials[i].first, trials[i].second,
|
||||
cycles, settings);
|
||||
} catch (...) {
|
||||
@@ -1416,6 +1423,10 @@ RefinementResult RefineModel(const gemmi::Structure &placed, const std::vector<M
|
||||
// of the best so far; the trajectory ends at its best step (on a deposited model, whose free set it was
|
||||
// fitted to, R-work falls while R-free rises, and it ends where it began).
|
||||
TrialOutcome best = state;
|
||||
// The trials of the step that went from the best so far: the final ladder goes from there too, and a trial
|
||||
// of the same weights from the same model is the same computation.
|
||||
std::vector<TrialOutcome> from_best;
|
||||
bool state_is_best = true;
|
||||
for (int step = 0; step < settings.max_steps && state.ran && !ctx.Cancelled(); step++) {
|
||||
const double w = state.trial.weight, adp = state.trial.adp_weight;
|
||||
std::vector<std::pair<double, double>> trials = {{w, adp}};
|
||||
@@ -1429,6 +1440,8 @@ RefinementResult RefineModel(const gemmi::Structure &placed, const std::vector<M
|
||||
}
|
||||
outcomes = RunTrials(trial_slots, trials, refinable, state.pos, state.b, &state.fit, settings.macro_cycles,
|
||||
settings);
|
||||
if (state_is_best)
|
||||
from_best = outcomes;
|
||||
k = choose(outcomes, fmt::format("step {}", step + 1).c_str());
|
||||
if (k < 0)
|
||||
break;
|
||||
@@ -1437,22 +1450,42 @@ RefinementResult RefineModel(const gemmi::Structure &placed, const std::vector<M
|
||||
if (!gains || !(t.within_bounds && t.r_free < best.trial.r_free + R_FREE_TIE))
|
||||
break;
|
||||
state = std::move(outcomes[k]);
|
||||
if (displaces(state.trial, best.trial))
|
||||
state_is_best = displaces(state.trial, best.trial);
|
||||
if (state_is_best) {
|
||||
best = state;
|
||||
from_best.clear();
|
||||
}
|
||||
}
|
||||
state = std::move(best);
|
||||
|
||||
// 3. The final weights: a short ladder from where the trajectory ended, tighter geometry first, its choice
|
||||
// kept where it displaces that.
|
||||
// kept where it displaces that. A rung the step from there tried already is taken from that step.
|
||||
if (state.ran && !ctx.Cancelled()) {
|
||||
const double w = state.trial.weight, adp = state.trial.adp_weight;
|
||||
std::vector<std::pair<double, double>> trials;
|
||||
for (double f : settings.final_ladder)
|
||||
if (w * f <= w_max)
|
||||
trials.emplace_back(w * f, adp);
|
||||
outcomes = RunTrials(trial_slots, trials, refinable, state.pos, state.b, &state.fit, settings.macro_cycles,
|
||||
settings);
|
||||
outcomes.assign(trials.size(), TrialOutcome());
|
||||
std::vector<std::pair<double, double>> missing;
|
||||
for (size_t i = 0; i < trials.size(); i++) {
|
||||
for (const TrialOutcome &o : from_best)
|
||||
if (o.ran && o.trial.weight == trials[i].first && o.trial.adp_weight == trials[i].second)
|
||||
outcomes[i] = o;
|
||||
if (!outcomes[i].ran)
|
||||
missing.push_back(trials[i]);
|
||||
}
|
||||
std::vector<TrialOutcome> ran = RunTrials(trial_slots, missing, refinable, state.pos, state.b, &state.fit,
|
||||
settings.macro_cycles, settings);
|
||||
for (size_t i = 0, m = 0; i < trials.size(); i++)
|
||||
if (!outcomes[i].ran)
|
||||
outcomes[i] = std::move(ran[m++]);
|
||||
k = choose(outcomes, "final");
|
||||
// The main slot's fit as it stands where its last rung ran on it, for the maps' Refit() to start from
|
||||
// (the solvent grid and the likelihood's D) whether the rung ran or was taken from the step.
|
||||
const size_t main_last = (trials.size() - 1) / trial_slots.size() * trial_slots.size();
|
||||
if (outcomes[main_last].ran)
|
||||
ctx.Restore(outcomes[main_last].fit);
|
||||
if (k >= 0 && displaces(outcomes[k].trial, state.trial))
|
||||
state = std::move(outcomes[k]);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user