Skip to content

Commit bdbc247

Browse files
trishortsclaude
andcommitted
refactor(rt): drop batched Chronologer PredictRetentionTimeEquivalents override
The override ran batched Torch forward passes for speed, but its results are not bit-identical to per-peptide prediction (~1e-7 drift from batched BatchNorm/matmul), which broke two pre-existing exact-equality tests (PredictRetentionTimeEquivalents_MatchesSinglePredictions and _LargeBatch) and contradicted its own "results identical to PredictCore" doc. It is not needed here: the method is declared on IRetentionTimePredictor and implemented in the base RetentionTimePredictor, so Chronologer now inherits the base implementation. The consumers (MetaMorpheus parallel search RT calibration) call the interface method and keep working; only a one-time calibration step loses the batched speedup. This file now matches master. Batched inference can return as its own PR with tolerance tests. Addresses nbollis #1036 review ("Is this method needed? ... use the existing method"). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 0a6b6b4 commit bdbc247

1 file changed

Lines changed: 0 additions & 78 deletions

File tree

mzLib/Chromatography/RetentionTimePrediction/Chronologer/ChronologerRetentionTimePredictor.cs

Lines changed: 0 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -80,84 +80,6 @@ protected override bool ValidateBasicConstraints(IRetentionPredictable peptide,
8080
}
8181
}
8282

83-
/// <summary>
84-
/// Batched override: formats/encodes the peptides in parallel (CPU) and runs the Chronologer model in
85-
/// large batched forward passes instead of one locked batch-1 call per peptide. The model is in eval
86-
/// mode (BatchNorm uses running statistics), so each peptide's prediction is independent of the batch —
87-
/// results are identical to <see cref="PredictCore"/>, just far faster for many peptides.
88-
/// </summary>
89-
public override IReadOnlyList<(double? PredictedValue, IRetentionPredictable Peptide, RetentionTimeFailureReason? FailureReason)>
90-
PredictRetentionTimeEquivalents(IEnumerable<IRetentionPredictable> peptides, int maxThreads = 1)
91-
{
92-
if (_disposed)
93-
throw new ObjectDisposedException(nameof(ChronologerRetentionTimePredictor));
94-
95-
var list = peptides as IReadOnlyList<IRetentionPredictable> ?? peptides.ToList();
96-
int n = list.Count;
97-
int encodedLength = ChronologerSequenceFormatSchema.EncodedLength;
98-
var results = new (double?, IRetentionPredictable, RetentionTimeFailureReason?)[n];
99-
var encoded = new long[n][]; // null when the peptide could not be encoded
100-
var reasons = new RetentionTimeFailureReason?[n];
101-
102-
// Phase 1 — format + integer-encode each peptide in parallel (pure CPU, no model access).
103-
System.Threading.Tasks.Parallel.For(0, n,
104-
new System.Threading.Tasks.ParallelOptions { MaxDegreeOfParallelism = Math.Max(1, maxThreads) }, i =>
105-
{
106-
var pep = list[i];
107-
if (!ValidateBasicConstraints(pep, out RetentionTimeFailureReason? basicReason))
108-
{
109-
reasons[i] = basicReason;
110-
return;
111-
}
112-
string? formatted = GetFormattedSequence(pep, out RetentionTimeFailureReason? fmtReason);
113-
if (formatted == null)
114-
{
115-
reasons[i] = fmtReason ?? RetentionTimeFailureReason.PredictionError;
116-
return;
117-
}
118-
var ids = new long[encodedLength]; // zero-padded
119-
for (int k = 0; k < formatted.Length; k++)
120-
{
121-
if (!CodeToInt.TryGetValue(formatted[k], out int v)) { ids = null!; break; }
122-
ids[k] = v;
123-
}
124-
if (ids == null) { reasons[i] = RetentionTimeFailureReason.PredictionError; return; }
125-
encoded[i] = ids;
126-
});
127-
128-
var valid = new List<int>(n);
129-
for (int i = 0; i < n; i++) if (encoded[i] != null) valid.Add(i);
130-
131-
// Phase 2 — batched model inference. One forward pass per chunk (model lock held once per chunk).
132-
const int chunkSize = 2048;
133-
lock (_modelLock)
134-
{
135-
using var noGrad = no_grad();
136-
for (int off = 0; off < valid.Count; off += chunkSize)
137-
{
138-
int m = Math.Min(chunkSize, valid.Count - off);
139-
var flat = new long[(long)m * encodedLength];
140-
for (int j = 0; j < m; j++)
141-
Array.Copy(encoded[valid[off + j]], 0, flat, (long)j * encodedLength, encodedLength);
142-
143-
using Tensor input = tensor(flat, dtype: ScalarType.Int64).reshape(m, encodedLength);
144-
using Tensor prediction = _model.Predict(input); // [m, 1]
145-
double[] preds = prediction.reshape(m).to(ScalarType.Float64).data<double>().ToArray();
146-
for (int j = 0; j < m; j++)
147-
{
148-
int i = valid[off + j];
149-
results[i] = (preds[j], list[i], null);
150-
}
151-
}
152-
}
153-
154-
for (int i = 0; i < n; i++)
155-
if (encoded[i] == null)
156-
results[i] = ((double?)null, list[i], reasons[i]);
157-
158-
return results;
159-
}
160-
16183
public override string? GetFormattedSequence(IRetentionPredictable peptide, out RetentionTimeFailureReason? failureReason)
16284
{
16385
failureReason = null;

0 commit comments

Comments
 (0)