OpenMS
Loading...
Searching...
No Matches
PercolatorReset Class Reference

Percolator-RESET: semi-supervised rescoring with FDR control at the peptide (stem) level. More...

#include <OpenMS/ANALYSIS/ID/PercolatorReset.h>

Classes

struct  AuditFeature
 
struct  Input
 Rows: one per PSM (all hits of all spectra), in any order. More...
 
struct  Options
 
struct  Output
 
struct  Stem
 One considered or dropped stem. More...
 

Public Types

enum class  Role : int { TARGET = 0 , ESTIMATING_DECOY = 1 , TRAINING_DECOY = 2 }
 Role of a row's stem (decoy stems by their coin) More...
 

Static Public Member Functions

static Output run (const Input &input, const Options &options)
 Runs RESET; throws Exception::InvalidValue on malformed input or without decoy stems.
 
static std::vector< double > seqStepQValues (const std::vector< bool > &is_decoy, double factor, double plus_one)
 
static bool isTrainingDecoy (std::uint64_t salt, const std::string &stem)
 The coin of a decoy stem: true = training decoy. Exposed for tests.
 

Detailed Description

Percolator-RESET: semi-supervised rescoring with FDR control at the peptide (stem) level.

Implements the single-decoy RESET procedure of Freestone, Käll, Noble and Keich (J Proteome Res 2025, "How to train a postprocessor ... while maintaining control of the FDR"; theory: Freestone, Noble and Keich, arXiv:2411.15771), as Percolator 3.07+ ('–reset-algorithm') and the authors' Python implementation do:

  1. PSM-level competition: per spectrum (Input::spectrum) the hit with the best search engine score.
  2. Stem reduction: per stem (unmodified sequence; Input::stems) the best of these PSMs by search engine score; it carries that PSM's features and label (label-blind: a sequence with target and decoy PSMs is one stem). Every stem is considered ("PSM-only" protocol of the paper: ProSE's decoys are reversed proteins, which give no fixed target-decoy peptide pairing).
  3. Coin: every decoy stem is a training decoy or an estimating decoy, by an independent Bernoulli(1/2) coin: bit 0 of a 64-bit hash of (salt, stem). Pseudo-targets = targets + estimating decoys.
  4. Options::iterations rounds (default 25; the paper uses 5, which do not converge on DIA pseudo-spectra): positive set = pseudo-targets that pass Options::train_fdr against the training decoys with SeqStep+ at c = 3/4 (decoys + 1 counted three times; without positives first without the +1, then with the threshold relaxed by 0.005 steps); negative set = all training decoys; class weights (C+, C-) from Options::c_values by 3-fold cross-validation maximizing the held-out pseudo-target discoveries (same SeqStep+), then a linear L2-SVM (Percolator's TRON solver) on all of the round's training rows rescores every stem. Only training-decoy labels enter (masking): estimating decoys are pseudo-targets throughout. The C grid uses all 9 pairs (the authors' Python restricts it to C- >= C+).
  5. Final ranking: training decoys are dropped; targets and estimating decoys are ranked by the last model and q-values follow from FDP = 2 (D_est + 1) / max(T, 1) (SeqStep+ at c = 2/3), monotonized from the bottom.

The stem-level q-values are the procedure's FDR-controlled output. Everything at PSM level is heuristic (see Output::psm_q_values).

Two-decoy mode (Options::decoy_databases = 2; the paper's two-decoy mode, Algorithms 3a/4 there and the authors' Python with mult = 2): the search ran against the targets and two decoy databases of the same construction, so the PSM-level competition of step 1 is the best of three and a null stem is a target, a decoy of set 1 or a decoy of set 2 with probability 1/3 each. The coin of step 3 is drawn for every decoy stem of either set, as in single-decoy mode. Then each training decoy faces on average two null pseudo-targets (one estimating decoy, one incorrect target): the positive set and the cross-validation use SeqStep+ at c = 2/3 (factor 2 instead of 3), and the final ranking FDP = (D_est + 1) / max(T, 1) (c = 1/2, factor 1 instead of 2). Input::decoy_set tells the two sets apart; it only checks that both are present (Output::decoy_stems_second_set: a set that never wins means the database had no second decoy set, and the factors would be wrong) and counts shared decoy sequences: a stem whose decoy rows (all rows of its sequence, also competition losers) come from both sets is one decoy for its hypotheses, which win as a decoy with probability 1/2 instead of 2/3; such stems count twice in the positive-set and final estimates (training and estimating decoy 1/4 each against 1/2 for the null target), and the final estimate's offset is then the largest weight, (sum of the weights + 2) / max(T, 1) (with + 1, a block of shared null hypotheses is anti-conservative). The detection needs the complete protein evidence of every decoy hit (all proteins that contain its sequence, as ProSE reports them): a shared sequence reported with the proteins of one set only counts once and its hypotheses are then under-estimated. Equal shares do not prove exchangeability: the two sets must be built so that a null hypothesis really has two independent decoys of the target's distribution (e.g. no sequences shared between the sets, the same multiplicity of sequences in both).

Feature-symmetry audit (Options::audit): before training, the training decoys are compared with an unlabelled null proxy, the pseudo-targets below the median search engine score of the training decoys (the training decoys are restricted to the same score range; the lowest third of the targets would be mostly correct targets in runs with many identifications). Per feature the AUC (Mann-Whitney) and the two-sample Kolmogorov-Smirnov statistic are reported, and a cross-fitted logistic regression on all features gives a multivariate AUC. Features with |AUC - 0.5| > 0.05, and the set if the multivariate AUC exceeds 0.55, are flagged; flags are reported, nothing is dropped. The null proxy holds the estimating decoys, too (about a third of its nulls), which attenuates real asymmetries by about a third.


Class Documentation

◆ OpenMS::PercolatorReset::AuditFeature

struct OpenMS::PercolatorReset::AuditFeature
Class Members
double auc = 0.5 P(training decoy > null proxy) + P(=) / 2.
bool flagged = false
double ks = 0.0 Kolmogorov-Smirnov statistic.
string name

◆ OpenMS::PercolatorReset::Options

struct OpenMS::PercolatorReset::Options
Class Members
bool audit = false

run the feature-symmetry audit

vector< double > c_values = {0.1, 1.0, 10.0} grid for C+ and C- (all pairs)
int decoy_databases = 1 decoy databases searched: 1 (default) or 2 (two-decoy mode, see the class description; needs Input::decoy_set)
int iterations = 25 training rounds (5 in the paper; they do not converge on DIA data)
string learner = "svm"

"svm" (linear) or "mlp" (small MLP ensemble, experimental)

bool mlp_crossfit = true

MLP cross-fitting: the stems (folds fixed by a hash of salt and sequence) are scored by the ensemble of the chosen C pair that did not train on their fold, in every round and at the end (no final fit); otherwise one final ensemble trained on all training stems scores everything, which ranks the training decoys it trained on too low

int mlp_epochs = 30

MLP training epochs (the first round, or every round without warm starts)

int mlp_hidden = 16 MLP hidden units (two hidden layers)
int mlp_members = 8 MLP ensemble size.
int mlp_norm_min_decoys = 1000

MLP cross-fitting: the fold ensembles' scores are normalized (as Percolator's folds: the fold's score at train_fdr against its training decoys -> 0, its median training decoy -> -1) only if every fold has at least this many training decoys (0 = always)

int mlp_warm_epochs = 3

MLP warm starts: > 0 = the networks of each cross-validation task and of the final model continue from the previous round for this many epochs (a stem keeps its fold over the rounds); 0 = trained afresh every round

string representative = "model"

"model" (default): after each round a stem is represented by its PSM with the best score under the new model; "search": by its PSM with the best search engine score (the paper). With "model" masking holds, but the FDR also needs null targets and decoys to be exchangeable over their whole PSM collections, including their sizes (a null target with many more PSMs than a decoy gains from the maximum); entrapment validation decides.

optional< uint64_t > salt coin salt (never pick the best of several salts); unset = a fresh random salt, recorded in Output::salt
int threads = 1 threads for the cross-validation solves
double train_fdr = 0.01 FDR for the positive set and for the cross-validation counts.
vector< string > zero_features = {"enzInt"} features set to 0 (target-biased, as in the paper)

◆ OpenMS::PercolatorReset::Output

struct OpenMS::PercolatorReset::Output
Class Members
vector< AuditFeature > audit
Size audit_decoys = 0
bool audit_flagged = false
double audit_multivariate_auc = 0.5
Size audit_proxy = 0

audit group sizes (training decoys, null proxy)

double audit_proxy_ratio = 0.0 null-proxy stems per training decoy in the audit (about 3 if all null)
Size decoy_stems = 0
Size decoy_stems_both_sets = 0
Size decoy_stems_second_set = 0

two-decoy mode: considered decoy stems whose representative is a decoy of the second set (about half of decoy_stems if the two sets are exchangeable), and those whose representative is in both sets (Input::decoy_set 3; should be rare: a decoy sequence shared by the sets is one decoy, not two, for its spectra)

Size discoveries_1pct = 0 target stems with q <= 0.01
Size estimating_decoys = 0
string learner
string learner_settings learner "mlp": its settings (for reports); empty for "svm"
vector< double > psm_peps

per row, heuristic PSM-level posterior error probability: f p / (1 - p) (at most 1; f = 2, or 1 in two-decoy mode, where shared decoy stems count twice), with p the estimating-decoy fraction of the ranked PSMs by isotonic (antitonic in score) regression. 1 where q is 1 by definition (other hits, training decoys).

vector< double > psm_q_values

per row, heuristic PSM-level q-value: per spectrum, the hit with the best search engine score is the PSM (the competition that selects the stems' PSMs, so a stem's representative is always reported); PSMs of training-decoy sequences (decoy rows whose sequence's coin is "training") are dropped, and the remaining PSMs (targets, estimating decoys) get q = min over lower ranks of 2 (D_est + 1) / max(T, 1) (two-decoy mode: (D_est + 1) / max(T, 1); with shared decoy stems, as the final ranking, (sum of the decoy weights + 2) / max(T, 1)). 1 for the other hits and for training decoys. Not FDR-controlled (multiplicity of PSMs per peptide).

vector< Role > roles per row: role of the row's stem
vector< string > rounds training summary per round: chosen C+, C-, held-out discoveries, positives
uint64_t salt = 0 the effective salt and the learner (for reports)
vector< double > scores per row: score of the final model (all rows, also those that lost a competition)
vector< double > stem_q_values per row: q-value of the row's stem (Stem::q_value)
vector< Stem > stems all stems (considered or not)
Size target_stems = 0
Size training_decoys = 0
vector< string > used_features features the learner used (non-constant, not zeroed)
vector< double > weights linear learner: raw-feature weights of the final model, bias last

◆ OpenMS::PercolatorReset::Stem

struct OpenMS::PercolatorReset::Stem

One considered or dropped stem.

Class Members
bool considered = false ranked (always true: PSM-only protocol; kept for the stem table format)
bool decoy = false

label of the search-score representative; fixed (masking) even if representative "model" later picks a PSM of the other label

double q_value = 1.0 RESET q-value (1 for training decoys and stems not considered)
Size representative = 0 row of the stem's best PSM (by search engine score)
Role role = Role::TARGET
double score = 0.0 final model score of the representative
double search_score = 0.0 search engine score of the representative
string stem unmodified sequence

Member Enumeration Documentation

◆ Role

enum class Role : int
strong

Role of a row's stem (decoy stems by their coin)

Enumerator
TARGET 
ESTIMATING_DECOY 
TRAINING_DECOY 

Member Function Documentation

◆ isTrainingDecoy()

static bool isTrainingDecoy ( std::uint64_t  salt,
const std::string &  stem 
)
static

The coin of a decoy stem: true = training decoy. Exposed for tests.

◆ run()

static Output run ( const Input &  input,
const Options &  options 
)
static

Runs RESET; throws Exception::InvalidValue on malformed input or without decoy stems.

◆ seqStepQValues()

static std::vector< double > seqStepQValues ( const std::vector< bool > &  is_decoy,
double  factor,
double  plus_one 
)
static

SeqStep(+) q-values of a ranked list (best first): fdp_k = min(1, factor (D_k + plus_one) / max(T_k, 1)), q = minimum of fdp over ranks >= k. is_decoy per rank. Exposed for tests.