# SigMA2 (sigma2) — the knowledge base for agents > SigMA2 predicts, from the somatic mutations of targeted panels and exomes (a handful of SNVs per > tumor), whether a tumor's point mutations carry a mutational signature, whether its mutations and copy > number resemble those of BRCA1/2-deficient tumors (HRD), and whether they resemble those of > microsatellite-unstable or POLE-mutated tumors (MMRD/POLE). Each is a prediction, not a functional or > clinical determination (/docs/sigma2/concepts/calls). Python package `sigma2`, release 0.0.16-alpha.20 (this document describes that release). > House terms: the HRD classifier is "SigMA2's HRD model"; no release-status word is used. > Human documentation: https://sigscape.org/docs/sigma2 . Package page: https://sigscape.org/packages/sigma2 . > The package installs from PyPI (`pip install --pre sigma2`); the repository is private while > the manuscript is in preparation. License: Harvard Academic and Non-Commercial Research Use. > This document states no performance numbers: a classifier's status states what its calls support. ## Pages (same order as the site) - Getting started: Overview /docs/sigma2 · Choose the analysis and check your panel /docs/sigma2/choose · Install /docs/sigma2/install · Quickstart /docs/sigma2/quickstart · Migrate from SigMA v1 /docs/sigma2/migrate - Core guides: 2. Detect a signature (SBS3, APOBEC, any other) /docs/sigma2/run · 1. Prepare your input files /docs/sigma2/inputs · 5. Read the output /docs/sigma2/outputs · 6. Plot spectra and exposures /docs/sigma2/plots · Worked example: fifty breast panels /docs/sigma2/tutorials/breast-panels · 3. Detect HRD /docs/sigma2/hrd · 4. Detect MMRD or POLE /docs/sigma2/mmrd · Worked example: HRD on exomes /docs/sigma2/tutorials/exome-hrd - Advanced guides: Adapt SigMA2 to your own data /docs/sigma2/adapt · Train a classifier /docs/sigma2/train · Curate training labels /docs/sigma2/curate · Models and the store /docs/sigma2/store · Worked example: train on your own genomes /docs/sigma2/tutorials/train-panel-classifier · Reproduce SigMA v1 /docs/sigma2/legacy · In sigscape studio /docs/studio/sigma2 - Concepts: What a call means /docs/sigma2/concepts/calls · /docs/sigma2/concepts/classifier · /docs/sigma2/concepts/status · - Detection law (/docs/sigma2/concepts/classifier#the-detection-law-and-a-cells-ceiling): per cell, from its out-of-fold calls at the loose cutoff, P(call | lambda) is logistic in log10 lambda, lambda = the positive's expected burden on the panel (genome exposure share x panel SNVs); LoD50 and LoD90 are read off it; the share of positives below them is the cell's ceiling (sigma2.tools.detection.detection_report, sigma2.plot.dtc). Known limits states it without the math (/docs/sigma2/concepts/limits#when-a-call-is-possible-and-when-it-is-not); exp_ in a result is the burden assigned per sample (/docs/sigma2/run#check-what-the-panel-delivered), and sigma2.plot.dtc(results_dir, tumor_type=, output=) draws the law for a training run of your own. - quick_simulation(input, tumor_type, seq_type, ref_genome, seed) -> DataFrame (0.0.16-alpha.20): SigMA v1's simulation of panels like a cohort from whole genomes of known Signature 3 status (columns: 96 channels, tumor, is_sig3, scale, main); score it and read a cutoff off the negatives (/docs/sigma2/legacy#simulate-panels-like-your-cohort). maf_percent= is refused. Also ported from SigMA v1 (/docs/sigma2/reference/api/sigma1): get_threshold(df, limits, var, signal, cut_var) and sen_fpr(df, var, signal), reproduced from R; match_training_genomes(data, tumor_type), reproduced; cluster_characteristics(out, tumor_type). Migration map: /docs/sigma2/migrate. - Python API (/docs/sigma2/reference/api): every function as one sentence, signature, parameters, Returns, a call as a guide runs it, and Used in (the guides that call it); one page per section. - Reference: /docs/sigma2/reference/api, one page per section (scoring, models, hrd, training, inputs, sigma1) (every public function, with an Advanced branch of every module at /docs/sigma2/reference/internals) · /docs/sigma2/reference/cli · /docs/sigma2/reference/classifiers · /docs/sigma2/reference/vocabulary · /docs/sigma2/reference/examples (the environment and the example files, with download links and an archive) · /docs/sigma2/reference/files · /docs/sigma2/reference/license · For AI agents /docs/sigma2/agents - Every guide opens with who it is for, what it produces and what it needs, and ends with what to read next; every code block is labeled Input (Python, Terminal or File), Output, Template or Signature, with an explanation; every documented call is followed by its parameter table (Parameter | Data Type | Description | Required). - The example files: https://sigscape.org/sigma2/examples/sigma2-examples.zip (segments, exposure template, README); the mutation files come from the SigMA (R) repository (URLs on the pages). Every Input block was replayed on a fresh pip install of this release with those files. ## Rules for an agent (keep these) 1. Pass tumor_type, seq_type and soi explicitly. seq_type="auto" chooses by SNV burden, reports the choice, and cannot tell panels of similar size apart (msk, fo, op). 2. Leave catalog= and snv_ctf= out unless the user asked for a value: the classifier's own apply (its card records both). Another catalog warns; one lacking the signature refuses. 3. Calls are pass_mva (10% FPR operating point) and pass_mva_strict (1%). Never threshold _mva yourself. MMRD/POLE calls are MMRD_category; never threshold the _prob columns. 4. NA is "no call", not negative. pass_mva_strict all NA = the classifier has only a loose operating point. Every call column NA = a score-only (experimental) classifier run with force=True. 5. force=True gives scores that rank samples within the cohort; no calls, no FPR guarantee. 6. Report the cell's status (Card.status: released | weak | experimental) and call_evidence (informative | weak) with any call. Do not report performance numbers from cards as claims. 7. Filter VCFs to PASS before scoring: every SNV record counts whatever its FILTER. 8. Record sg2.__version__, tumor_type, seq_type, soi, the floor and the catalog with any table. 9. A folder of VCFs names each sample by its file name without .vcf/.vcf.gz (T1.vcf.gz -> T1); same-named files in different folders keep their paths. Index other tables by those names. 10. Nothing below the SNV floor is scored (5; prost 4; osteo and panc_en 3; or the card's). 11. readjust=True is for whole cohorts off the training depth, never a single patient, about 30+ samples; the package enforces no minimum, and the option is off by default. 12. HRD without segments runs the weaker nogis variant: report the variant. HRD-low is indeterminate: the pan-cancer and the tumor-type model disagree (hrd_call HRD = both > 0.5, HRP = neither, HRD-low = one). segments= restricts nothing (tumors without segments are scored without copy number); pass mxw="max" on cohorts. ## What a call means (/docs/sigma2/concepts/calls) - Signature call: reads the 96-channel SNV spectrum only (no indels, copy number or genes). Supports: the point mutations carry the signature's pattern. Does not establish the cause; a Signature 3 call is not a BRCA1/2 test and not an HRD determination. - HRD call: SigMA v1's Signature 3 score/likelihood/exposure + deletions >= 5 bp with/without >= 2 bp microhomology + copy-number scars (HRD-LOH, TAI, LST; or fga, lst_lr) when segments are given; trained two-step on biallelic BRCA1/2 tumors + 3 nearest wild-type neighbors each (Gulhan et al. 2025 preprint, 10.1101/2025.10.13.681986). Supports: resemblance to BRCA1/2-deficient tumors of the training cohorts. Does not establish current HR function (scars and signatures persist after repair is restored). Report it beside the Signature 3 call, never in its place. - MMRD/POLE call: MMR and POLE signature likelihoods/cosines/exposures + microsatellite indel counts; trained against MSIsensor (MSK-IMPACT) and MANTIS/MSIsensor (TCGA) calls. Supports: resemblance to tumors a sequencing MSI caller called unstable. Not an IHC (MLH1/MSH2/MSH6/PMS2) or PCR result. ## Install pip install --pre sigma2 # Python >= 3.9; Linux x86_64/aarch64, macOS arm64, Windows x64 (MS C++ Build Tools; plain-text FASTA) sigma2 data genome hg19 # ~3 GB from UCSC into the data dir; or pass ref_genome= sigma2 info # version, data dir, assets pip install --pre 'sigma2[panel]' # + bedtools binary on PATH: only for training (panel simulation) sigma2 data fetch --all # six repeat-locus BEDs (~835 MB) for microsatellite indel counts # (MMRD/POLE, the HRD pre-step); private repo -> needs a GitHub account the maintainers # gave access (gh auth login or SIGMA2_GITHUB_TOKEN). Same six files, same # sha256: github.com/parklab/SigMA inst/extdata/repeat_bed_files (git lfs), e.g. # curl -L -o "$(sigma2 data path repeat_bed_files-.bed)" https://media.githubusercontent.com/ # media/parklab/SigMA/master/inst/extdata/repeat_bed_files/.bed ; then sigma2 data verify. WITHOUT THEM # NOTHING STOPS: MMRD/POLE scores zero microsatellite indels, HRD skips # its MMRD/POLE step, silently at verbose=0. Check nmsi_* and MMRD_category. Data dir: $SIGMA2_DATA_DIR else per-user app dir (~/.local/share/sigma2 on Linux): reference/_genome.fa, repeat_bed_files/, sysdata/models/ (package classifiers seeded + yours), sysdata/hrd/ (tune_hrd), sysdata/add/, sysdata/custom.h5, cache/. Unwritable dir: classifiers are read from the wheel; training/saving/caches have no fallback. scikit-learn 1.4 <= v < 1.8 (pickles). A C toolchain is needed at install (one dependency compiles). SIGMA2_DECOMP_CACHE memoizes decompositions (atomic writes; entries are pickles: share only with trusted people). ## Scoring API import sigma2 as sg2 out = sg2.run(input=..., ref_genome="hg19", tumor_type=..., seq_type=..., soi=..., **options) # run(**kwargs) and predict_mmrd_ppd(**kwargs): every argument BY NAME (a positional call raises TypeError). # tumor_type and soi are required (no soi -> KeyError 'soi'; soi="auto" finds no classifier). df = out.df # one row per SCORED sample; below-floor samples are absent out.to_lite() # short table with categ (a DataFrame) out.input_matrix.df # every sample read; verbose=2 lists the below-floor ones with their counts out.catalog_name, out.seq_type, out.soi, out.model, out.snv_floor (the floor applied), out.snv_ctf (the override, else None) run() arguments: - input: MAF path (.maf/.maf.gz), VCF path(s) (.vcf/.vcf.gz/.vcf.bgz), list of VCFs, directory of VCFs, CSV path or DataFrame of SBS-96 counts (no genome needed for a matrix). - tumor_type (required): one of 39 names, any case. Sets the signature set, the cluster centroids and the classifier. Short names: prost crc lymph eso head lgg panc_ad panc_en medullo osteo lms meso bone_other; endometrial = uterus. msi and pole are signature-set pseudo types, not inputs. - seq_type: msk | fo | op | seqcap | tcga_mc3 | wgs have classifiers; seqcap_probe and wgs_pancan legacy only; msk_on_target ampliseq illu_hotspot illu_trusight invitae_* are simulation BED names only (no bundled BED). "auto" picks by burden and reports. Custom panel names are NOT accepted: score as the closest bundled platform. - soi: COSMIC SBS name (SBS3, SBS8, SBS18, SBS7a, ...), composite preset APOBEC(SBS2,SBS13) SBS7(a-d) SBS10(a-d) SBS17(a,b) CLOCK(SBS1,SBS5), alias UV=SBS7 POLE=SBS10, or "HRD". Any case. One signature per run; loop over cards() for several. - ref_genome: "hg19" | "hg38" | "GRCh37" | "GRCh38" | FASTA path. Not needed for a matrix. - model: "auto" (released cell or refusal) | "legacy" (SigMA v1 ported) | a loaded model (sg2.model("breast/SBS3/msk") or sg2.model(name, root_dir=store)); centroids passed with it. - catalog: cosmic_v3p2_inhouse | cosmic_v3_inhouse | cosmic_v2_inhouse | cosmic_v3p2 (cosmic_v2 exists but has no tissue sets and is refused). Default: the classifier's own (SigMA2 cells cosmic_v3p2_inhouse; legacy and MMRD/POLE cosmic_v2_inhouse). Different -> warning (printed at verbose=0). - snv_ctf: int floor override. Default order: snv_ctf= > card snv_cutoff > tumor type floor. - readjust: bool, re-derive cutoffs for the batch's depth; adds cutoff_readjusted. Falls back with a warning when: no table on the card, too few effective negatives, batch outside the negatives' depth range, legacy models. - force: bool, use an experimental cell (scores only). - verbose: 0 (errors and some warnings: catalog, readjust) | 1 (the run and the other warnings, e.g. legacy features passed as missing, indel counts left empty) | 2 (also lists the samples below the floor). - mxw: workers for the decomposition and the HRD pre-step (int or "max"; pass "max" for HRD cohorts). name: label. - HRD: segments=, gis=, indels=, beds=, hrd_model=. Advanced/legacy: mva, assign, msi, wcf. - Internal: cluster_fractions, tumor_catalog, min_del_size, min_mh_size, norm96, steps. Refusals (ValueError, message names the fix): - no classifier for the cell: 'No classifier for tumor_type="breast", soi="SBS3", seq_type="seqcap_probe". breast SBS3 has one on: fo, msk, op, seqcap, tcga_mc3, wgs. SigMA v1 has one for this cell: pass model="legacy". sg2.describe() lists every classifier.' - experimental cell: "the only classifier for ... is EXPERIMENTAL: withheld by the release gate (...). Pass force=True to use it anyway; its scores rank samples, but pass_mva and the confidence labels from it are not supported." - catalog lacking the signature; a catalog with no signature set for the tumor type ('catalog "cosmic_v2" has no signature set for tumor_type="breast" ... Catalogs with one: cosmic_v2_inhouse, cosmic_v3_inhouse, cosmic_v3p2, cosmic_v3p2_inhouse.'); a wrong catalog SigMA2 chose itself ("this is a bug"). - HRD on a tumor type its model cannot score (see HRD). Log lines (verbose=1): Classifier: tumor_type="breast", soi="SBS3", seq_type="msk" (verified) Input: 50 samples → 38 after filtering (12 dropped) Using classifier: breast/SBS3/msk seq_type="auto" chose "msk": the input's median is 8 SNVs, the msk classifier's training median 9 (others: ...) Using SBS3 classifier from SigMA v1 (legacy, native Python) catalog X is not the one the classifier was trained on (Y). Its scores and calls are ... not the validated ones; leave catalog= out to use Y. HRD: no copy-number input (segments= or gis=); scoring with the model trained without copy number. MVA::readjust breast/SBS3/msk: cutoffs readjusted to this cohort's median of 11.0 SNVs (trained at 9.0): FPR 10% cutoff 0.4477 -> 0.4417 ## Output columns (out.df) Read these: total_snvs; _mva (score in [0,1]); pass_mva (loose call, 10% FPR; required-motif rule applied); pass_mva_strict (strict call, 1% FPR; NA when no strict cutoff); confidence (high | low | negative | NA); call_evidence (informative | weak, from the card); exp_, rat_ (exposure, exposure fraction); sigs_all, exps_all (best decomposition, '.'- and '_'-joined; SigMA2 names, SBS3.SBS8 for v1's Signature_3.Signature_8; unpack: dict(zip(s.split('.'), map(float, e.split('_')))) per row); cutoff_readjusted (readjust=True only); motif_count (lung APOBEC msk only). Internal: 96 channels a[c>a]a ...; _c, _l_rat, _c_diff, _wl; _high_k_ml, _low_k_ml; max_ml, sig_max_ml, max_c, sig_max_c, max_wl, sig_max_wl; comb_all_c, comb_all_l; pass_ml (SigMA v1 likelihood call; do not report). Semantics: both calls True -> confidence high; loose only -> low; neither -> negative. No strict cutoff: pass_mva_strict NA, confidence negative below the loose cutoff and NA above. force=True: all call columns NA. Samples below the floor are dropped from out.df for every input format (they stay in out.input_matrix; verbose=2 lists them). to_lite(): total_snvs, _ml per signature, _c, exp_, rat_, _l_rat, _mva, pass_mva, pass_mva_strict, confidence, sigs_all, exps_all, pass_ml, categ. categ: MMRD | POLE | an MSI/POLE signature | _hc (strict) | _lc (loose) | most likely or most exposed other signature. On predict_mmrd_ppd(...).to_lite(), categ can read msi/Signature_msi for a tumor called MMRP (4 of the 50 example panels): read MMRD_category from .df. Required motif: only lung/APOBEC/msk (TCW>T/G >= 1); below it calls False, confidence negative, score unchanged, motif_count added. ## Input contracts MAF (.maf, .maf.gz): required Chromosome, Start_Position, Reference_Allele, Tumor_Seq_Allele2 and/or Tumor_Seq_Allele1, Tumor_Sample_Barcode. Allele per row: Allele2, else Allele1 when Allele2 equals the reference. Variant_Type present -> only SNP rows are SNVs; indels from DEL/INS. NCBI_Build checked against ref_genome (GRCh37 accepted for hg19). TRUE -> T. chr prefix optional; 23/24 = X/Y. End_Position and Strand optional (strand always +). Single-tumor MAF works. Pass a MAF as a PATH (a DataFrame is read as a matrix: 'no 96-channel columns found'). Reference_Allele is NOT compared with the genome: 0-based positions or another build without NCBI_Build are scored SILENTLY (a one-base shift of the example changed 16/38 calls); compare REF with the genome first (/docs/sigma2/inputs#check-the-reference-alleles). Mutations off chromosomes 1-22, X, Y (MT, unplaced contigs) are dropped without a message. VCF: .vcf.gz must be bgzip (gzip -> 'seek not implemented'). Every SNV record counts whatever its FILTER (filter to PASS first); indels only PASS or '.'. Multi-allelic split per ALT. Several sample columns with GT -> one sample per non-ref column named by the column; tumor/normal pair (normal 0/0) -> one sample named by its file name without the extension; two columns without GT -> one sample, no message at verbose=0. Directory -> one sample per file, named the same way (same-named files in different folders keep their paths). IDs are strings. Matrix (CSV/DataFrame): samples as rows; channels found BY NAME in any spelling/case (A[C>A]A, a[c>a]a, caaa); missing/repeated channel refused with the list; transposed matrix flipped; other columns kept; IDs from index / first text column / 'tumor'. MMRD/POLE on a matrix needs nins, ndel, nmsi_ins, nmsi_del (error names the missing ones, except ndel: KeyError 'ndel'; sigma2.indel_features computes them). Missing value: float/CSV blank -> ValueError 'Encountered all NA values'; Int64 NA -> TypeError. indel_features(indels=, ref_genome=, samples=): the indel table's Sample MUST equal the matrix index; an unmatched name is counted for no tumor, SILENTLY (zeros with samples=, NaN after the join without; MMRD/HRD still score: the example's MMRD tumor becomes MMRP). Check set(indels.Sample) - set(matrix.index) == set(). Multi-allelic ALT ('CT,CTT') skipped silently: split first. MAF-style positions: a deletion at its first deleted base, an insertion at the base before it. Segments (HRD): sample column required; names matched in any case: sample|id|tumor|tumor_sample_barcode| patient|sample_id, chr|chrom|chromosome|seqnames, start|startpos|start_position|loc.start|start.pos, end|endpos|end_position|loc.end|end.pos. Allele-specific: ASCAT nMajor/nMinor; FACETS tcn.em/lcn.em or tcn/lcn; PURPLE majorAlleleCopyNumber/minorAlleleCopyNumber/copyNumber (fractional rounded); plain major/minor, ncopy_*. Log-ratio (.seg, seg.mean + aliases) -> tcn variant; allele-specific with purity/ploidy converted to log ratio when needed. No recognized column -> error listing names; copy number matching no tumor -> warning. gis= is a per-sample Series/dict (a GIS sum alone falls back to a weaker variant with a warning). Tumors without segments are scored without copy number. Builds: SNV spectra hg19/hg38; segments hg38 OK except the msk HRD grid (hg19, refuses hg38 with instructions); repeat loci hg19 only (pass beds= for hg38 or lift indels); bundled panel BEDs hg19. ## Floor, cutoffs, catalog (rules) Floor order: snv_ctf= > card snv_cutoff (every shipped signature classifier records it; all equal their tumor type's floor) > get_snv_cutoff(tumor_type) = 5, prost 4, osteo 3, panc_en 3. HRD and MMRD/POLE apply the tumor type's floor. describe() heads each tumor type with its floor. Cutoffs: loose = 10% FPR on held-out negatives -> pass_mva; strict = 1% -> pass_mva_strict. Repeated CV; released cutoff = median over repeats (cutoff_rule median_repeat for shipped cells). Catalog: default the classifier's. Validity: the only catalogs with tissue signature sets are cosmic_v2_inhouse, cosmic_v3_inhouse, cosmic_v3p2, cosmic_v3p2_inhouse. A catalog with no signature set for the tumor type (cosmic_v2) is refused. Legacy (model="legacy") FIDELITY: reproduces R (short table + Signature 3 columns to 1e-13, every call) only for the 30 legacy cells that read every feature (msk/msk_cf/seqcap breast among them). 56 of the 86 that run read a feature the port passes as MISSING while R computes it (rat_sig3 in 42, an APOBEC cluster in 24, clock in msk and msk_cf osteo, oxi in tcga_mc3 skin): scores/calls can differ from R (tcga_mc3 breast on 1,018 TCGA exomes: 8 loose, 3 strict calls differ; supplying the feature reproduces R to 1e-14). verbose=1 names the missing features (its 'matches SigMA v1' remark is not borne out). Other signatures' l_rat/c_diff differ from R up to 0.19 (0.41 in msi columns). wgs/kidney and wgs_pancan for a non-breast tumor stop with IndexError. Per cell: /docs/sigma2/legacy#check-the-legacy-grid. HRD's Signature 3 features come through this port too. Legacy settings: catalog cosmic_v2_inhouse; weight_cf prior forced on for seqcap and seqcap_probe, user's choice on msk (wcf=True = R's msk_cf); floor is the TUMOR TYPE's (R used a flat 5: pass snv_ctf=5 on prost/osteo/panc_en to match R); MMRD via predict_mmrd_ppd(model="legacy") for msk and tcga_mc3 only. Legacy output has no call_evidence. ## Status model (the only claims) released (●): passed the gate -> a positive call at the stated FPR. 39 breast/lung cells were released BEFORE the gate (card release_gate "fpr<=0.1"): no evidence grade, no call_evidence column (classifiers page: "released before the gate"); 2 prost cells passed a single-run gate. HRD and MMRD/POLE models: cross-validated, no gate. released, weak evidence (◐; Card.status "weak"): FPR controlled, a positive call raises the odds little; call_evidence="weak" + warning -> "a reason to look, not a finding". experimental (▲): withheld; run() refuses without force=True; then scores with NA calls -> ranking only. wgs cells: threshold the exposure fraction rat_; not panel classifiers. legacy: SigMA v1 reproduction. MMRD/POLE: one SigMA2 model per platform (msk tcga_mc3 seqcap fo op); MMRD_category = the most probable class (the per-class 1% FPR cutoffs never overrule it). HRD: own model (more than 10 step-1 positives = BRCA1/2 tumors + 3 neighbors each, so as few as 3 BRCA1/2 tumors) or pooled model (other types' data); calls HRD | HRD-low | HRP; no gate, no weak tier. Its Signature 3 features come from SigMA v1's classifier (model="legacy", cosmic_v2_inhouse); it needs indels (MAF/VCF or mmej/nhej columns). Gate (repeated CV, as shipped): withheld only if (a) 10% FPR not certifiable on distinct negatives (< 30, rule of three), or (b) operating point unstable under resampling, or (c) median realized FPR over repeats exceeds target by more than one negative, or (d) LR+ lower bound <= 1. Otherwise released; graded weak if positives < 10 or LR+ below the bar. Single-CV gate (cv_repeats=1): FPR within target, LR+ >= 5 with lower bound > 1, >= 10 positives; failing withholds. Outside publish(), for the PCAWG-trained cells only: held-out single-input bar; SBS3 matching SigMA v1 (release scripts). Known cases: breast SBS3 msk/tcga_mc3 calls strong SBS3 only (not weak SBS3); crc SBS8 on fo/msk/op experimental; crc SBS18 calls summed SBS18+SBS36; medullo SBS3 does not indicate HRD (its positives' median GIS 6 vs 5 in negatives; breast 60 vs 22). breast SBS3 tcga_mc3 card: known_limit, exome panels simulated at hg19 coordinates of hg38-aligned genomes, to be retrained. ## Bundled classifiers (alpha.20; generate with sigma2.classifiers.cards()) Counts: 210 signature cells (127 released, 61 weak, 22 experimental) over 14 tumor types × platforms fo msk op seqcap tcga_mc3 wgs; HRD models for fo msk op seqcap tcga_mc3 (23 tumor types each, own or pooled); MMRD/POLE for the same five; 92 legacy SigMA v1 classifiers. Tumor types with signature cells: breast crc eso kidney lgg liver lung lymph medullo ovary panc_ad panc_en prost skin stomach (+ HRD/MMRD-only: adrenal biliary bladder cervix glioma head leuk meso thyroid uterus). Signatures with cells: APOBEC SBS1 SBS12 SBS16 SBS17 SBS18 SBS22 SBS28 SBS3 SBS30 SBS34 SBS36 SBS37 SBS4 SBS40 SBS41 SBS7 SBS8 SBS84 SBS85 SBS89 SBS9 SBS93. HRD coverage (all five platforms): adrenal biliary bladder breast cervix crc eso glioma head kidney leuk lgg liver lung lymph meso ovary panc_ad prost skin stomach thyroid uterus. Own model on msk: bladder breast cervix crc head lung ovary panc_ad prost skin stomach uterus; on fo/op/seqcap/ tcga_mc3: bladder breast cervix head lung ovary prost skin stomach testicular uterus (others pooled). HRD refuses (seen in training, no signature set): sarc on msk; paraganglioma sarc testicular thymoma uveal on fo op seqcap tcga_mc3. Never trained (refused): bone_other ewing liposarc lms medullo myeloma oligo osteo panc_en piloastro. HRD model sig3 settings: SigMA v1 SBS3 classifier (model legacy) on cosmic_v2_inhouse. Legacy SBS3 grid: msk (11 types) seqcap (12) seqcap_probe (12) tcga_mc3 (21) wgs (20) wgs_pancan (breast, generic); msk_cf variants; MMRD multinomials msk + tcga_mc3. Per-cell status: sigma2.classifiers.cards() or https://sigscape.org/docs/sigma2/reference/classifiers ## HRD (soi="HRD") run(soi="HRD", segments=...) -> columns: sig3_score sig3_ml sig3_exp; gis hrd_loh tai lst (gis variant) or fga lst_lr (tcn variant); mmej nhej; hrd_group (tumor type or pooled); hrd_variant (gis | tcn | nogis); hrd_score_pan hrd_score_type hrd_score; hrd_call (HRD | HRD-low | HRP | MMRD | POLE); MMRD_category; HRD_mva pass_mva pass_mva_strict confidence (for HRD); SBS3_pass_mva SBS3_pass_mva_strict SBS3_confidence (the Signature 3 step = SigMA v1's classifier). pass_mva is True for HRD AND HRD-low (confidence low): read hrd_call. HRD runs keep EVERY sample; below the floor: Signature 3 features NA, MMRD_category NA, mmej/nhej shown NA though scored; the call is not an assessment (without segments it is the model's prior, HRP): leave such tumors out of HRD prevalence. msk truth has no germline status (carriers among negatives); fo/op/seqcap learned copy number from SNP-array segments. Training: /docs/sigma2/hrd#reference-what-each-hrd-model-was-trained-on Pre-step: when indel counts exist, the platform's MMRD/POLE classifier runs first; MMRD/POLE tumors get that label as hrd_call and NA pass_mva/confidence (HRD_mva keeps a score that does not apply). Without the repeat-locus BEDs it is SKIPPED SILENTLY (no MMRD_category column). Variants by input: allele-specific segments -> gis (features sig3_score sig3_ml sig3_exp hrd_loh tai lst mmej nhej); log-ratio -> tcn (… fga lst_lr mmej nhej); none -> nogis (sig3 + mmej nhej). Two-step: feats = sg2.hrd_features(input, tumor_type, seq_type, segments=, indels=, gis=, ref_genome, model, beds, sig3) ; sg2.score_hrd(feats, seq_type, model, tumor_type, mmrd). Also sg2.genomic_instability(segments, genome) -> hrd_loh tai lst gis; sg2.copy_number_instability(segments). tune_hrd(features, labels="brca", seq_type, tumor_type="tumor_type", min_positives=10, neighbors=3, cv=5, seed=0, sig3=None, name=None, save=True, description=None, gis_grid=None, grid_genome=None, variants=None, complete=None): >= 2 positives; CN variant needs >= 50 rows; cross-validated only (no gate, no experimental tier); saved to /sysdata/hrd/. Guidance: prefer HRD when segments exist; SBS3 when SNVs only or for v1 comparability. Report hrd_call with SBS3_pass_mva as support. SBS3 positive + HRP = discordant, look at the spectrum. Cohorts: ~1 s per tumor on one worker (mostly the pre-step); pass mxw="max". CLI: sigma2 hrd score|features|gis|tune|models. ## MMRD and POLE mm = sg2.predict_mmrd_ppd(input, ref_genome, tumor_type, seq_type, model="auto"|"legacy", indels=, beds=, snv_ctf=, verbose=). Sets catalog cosmic_v2_inhouse, soi SBS3, mva False itself. Platforms: msk tcga_mc3 seqcap fo op (SigMA2); legacy msk tcga_mc3; no wgs. Needs indels (MAF/VCF, indels=, or nins/ndel/nmsi_ins/nmsi_del columns) and the repeat-locus BEDs (hg19). Columns: MMRD_category (MMRD | POLE | MMRP) — the most probable class; below-floor tumors are absent from the table; Unclassified only from legacy (no cutoff row for the platform). Training truth: MSIsensor (msk), MANTIS+MSIsensor (TCGA); MSI + pathogenic POLE hotspot labeled MMRD (an MMRD call does not exclude a pathogenic POLE EDM). The example's MMRD panel P-0007087 was an MSIsensor-stable training row. MMRD_prob POLE_prob MMRP_prob (scales differ SigMA2 vs legacy; never threshold them). nins ndel nmsi_ins nmsi_del. max_clust subtype_1..4 max_subtype = SigMA v1 MSI/POLE cluster subtypes (A–J, La/Lb/Ma/Mb, POLE), informational. ## Store and models sg2.describe(root_dir=None, format=None, verbose=0); sg2.models(tumor_type, soi, seq_type, root_dir) -> names like 'breast/SBS3/msk' (experimental/... for withheld); sg2.model(name|parts, root_dir); sg2.verify_model(...) -> True or ValueError; sg2.delete_model(..., force=False); sg2.models.migrate(dry_run=True, root_dir=None) (retired.json); classifiers.cards(root_dir, tumor_type, classifier, seq_type) -> Card(tumor_type, classifier, seq_type, kind signature|hrd|mmrd, status released|weak|experimental, verified, trained, snv_cutoff); classifiers.mmrd_platforms(), after from sigma2 import classifiers (not an attribute of import sigma2). Model dir: model.joblib imputer.joblib centroids.csv metadata.json (card) meta.json (SHA-256). Layout /// under /sysdata/models/ (+ experimental/), and in any Add.export(output_dir) directory (= a store: sg2.model(name, root_dir=dir)). Share = copy the cell dir. Card public fields: name status verified kind catalog snv_cutoff snv_cutoff_source training_depth evidence evidence_reason label_curation readjust_recommended readjust_note required_motif created_at. Internal: gate fields, cutoffs, CV settings, performance fields, nested meta (score_only, curation, ...). model.config is the card as a dict; model.meta the tuner's record. sg2.get(key) read-only: "sig_catalogs/", "indices/tumor_types", "sigs_per_tissue", "cosmic_catalogs/", "snv_counts", "mva_cutoffs", "tumor_sig_catalogs/" (a literal * returns None). sg2.set/clear advanced. ## Training (Add) Need: WGS somatic SNVs (PASS; exclude MMRD/POLE genomes) as VCF folder or MAF; exposures table (samples × every catalog signature; counts or fractions; refit on the training catalog, default cosmic_v3p2_inhouse); sample names equal (VCF folder -> paths); >= 30 distinct negatives and ~10 positives per signature surviving the floor (feasibility screen used 10 pos / 30 neg / 40 scorable per platform); sigma2[panel] + bedtools. genomes = sg2.Samples("vcf_dir/", ref_genome="hg19") # genomes.df: 96 ch + total_snvs; index = paths a = sg2.Add(genomes, exposures=ex, tumor_type="breast", seq_type=["msk","fo","op","seqcap","tcga_mc3"], soi=["SBS3","APOBEC"], catalog="cosmic_v3p2_inhouse", verbose=1) a.annotate("gauss") # bins: 0 negative, P positive bands; n_components=, min_exposure=, below="negative"|"exclude" a.label_report(); a.cluster(method="elbow"|"distance", distance_threshold=0.3) a.simulate(scaling_factor=2.0, addition_source=None, do=None, include_wgs=True) # bundled BEDs hg19; exomes need no top-up a.sparse_report(); a.save("x.pkl"); a.save("x.json") # json = studio editor export; cannot simulate() after load a.tune(estimator="monologit", cross_fit=True, cv_repeats=20, cutoff_rule="median_repeat", output_dir="results") a.publish(result_dir="results") # into the data dir behind the gate (withheld -> experimental/); or a.export("my_store") # any dir, no gate tune(target_fpr=0.1, mxw="max", do=None, output_dir=None, force=False, snv_cutoff=None, snv_constraint=None, max_snv_cutoff=12, snv_candidates=None, estimator="gbm"|"hist"|"monologit"| "monologit_ctx", monotonic=True, stratify_by=None, stratify_unknown="drop", capture_redraws=0, cv_repeats=1, cutoff_rule="mean_score"|"median_repeat", cross_fit=...). Package defaults = gbm, 1 repeat, mean_score, no cross-fit. Most shipped panel cells (113 of 154 released in 0.0.16-alpha.20): cross_fit=True, 20 repeats, median_repeat, learner mostly monologit; the other 41 cards record none of these fields. A card's config["cross_fit"], config["cv_repeats"], config["cutoff_rule"] and type(model.estimator).__name__ say which. publish(force=False, skip=None, result_dir=None, precision_bar=None, publish_experimental=True, min_lr=5.0, min_lr_lower=1.0, min_pos=10). export(output_dir, force=False, skip=None). Other: Add.load(path .pkl|.json|(H,W), tumor_type=, seq_type=, soi=, catalog=, ref_genome=); apply(json, only=); inject_simulations(sims {panel}.csv or dict, do, panel_aliases (mc3->tcga_mc3 built in), ref_genome, include_wgs, keep); extend(new_soi); split(test_size=0.25); drop_duplicates(); merge(other); fractional_exposures(); filter_sparse(); export_threshold_table(output_dir, floors, objective, legacy_lock=("SBS3",), stratifier); record_provenance(); label_definition(); supplement_from(...) (advanced; usually harmful; seed sweep + negative-donor control); contaminate(). sigma2.tools.simulator.calibrate_scaling_factor(base_burdens, reference_burdens, grid=None, metric="wasserstein"). Resources (16-core server): object build 1–6 min for 23–233 genomes (~15 min for 322); one candidate at 5 repeats a few minutes, at 20 repeats ~15–30 min on panels/exomes, longer on wgs; ~1 GB per candidate. mxw sets workers; LOKY_MAX_CPU_COUNT caps shared machines. Result tree: /README.md; /manifest.json training_log.csv summary.pdf ...; /// training_summary.json (releasable release_reason release_rule certifiable_fpr operating_point_stable snv_cutoff cv_repeats cutoff_rule ...), cv_scores.tsv (cv_score_r, fold_r), eval_scores.tsv, roc_curve.tsv, roc_curve_mean_score.tsv, roc.pdf cv.pdf confusion.pdf, training_data.tsv, training_data_crossfit.tsv, filtered_samples.tsv, panel_simulations.tsv, sample_assignments.tsv, confusion_samples.tsv. Curation vocabulary (label_curation on cards; release scripts, not a package API): strict all weakneg floor neg_bands exclude_bands merge ambiguous_negatives artifacts scars donors thin scale recluster; model axes estimator feature_set cluster n_components. Hand path = studio editor + Add.apply(json). Recluster after any label change; never label from W·H reconstructions. Add JSON export blocks: df obs bins clusterInfo (required) metadata signatures samples clusteredSignatures mutations; optional simulations, tuning (manifest + training_log). No schema version yet; core blocks stable in practice, tuning provisional. ## CLI sigma2 info · sigma2 data list|fetch [--all] [--group G] [--force] [name...]|verify|genome [hg19|hg38] [--force]|path NAME · sigma2 hrd score --input --tumor-type --seq-type [--ref-genome] [--indels] [--segments] [--model] [-o] [--all-columns] [--verbose 0|1|2] · sigma2 hrd features ... · sigma2 hrd gis --segments [--genome hg19|hg38] [-o] · sigma2 hrd tune --features --seq-type [--label-column] [--tumor-type-column] [--name] [--min-positives] [--neighbors] [--cv] [--seed] [--sig3-model] [--sig3-catalog] · sigma2 hrd models · sigma2 export-threshold-table --results DIR [--tumor-type] [--objective] [--legacy-lock SIG...] [--stratifier SIG]. No CLI for run()/predict_mmrd_ppd/Add. Library calls never download; a missing asset raises sigma2.registry.MissingAsset naming the command, except the repeat-locus BEDs: without them MMRD/POLE and the HRD pre-step proceed silently (see Install above). ## Worked examples (verified on alpha.20; values are example scores, not performance claims) Data: SigMA (R)'s 50 MSK-IMPACT breast panels https://raw.githubusercontent.com/parklab/SigMA/master/inst/extdata/examples/test_mutations_50sample.maf and TCGA breast exomes .../inst/extdata/examples/tcga_mc3_brca.maf (SigMA's license). out = sg2.run(input=MAF, ref_genome="hg19", tumor_type="breast", seq_type="msk", soi="SBS3", verbose=1) # 50 samples -> 38 scored (12 below 5 SNVs); e.g. P-0008709-T01-IM5 18 SNVs SBS3_mva 0.9165 pass_mva True # pass_mva_strict True confidence high; P-0007087-T01-IM5 43 SNVs 0.7806 True False low sg2.run(..., model="legacy") # catalog cosmic_v2_inhouse; P-0007087: 0.4109, no call sg2.run(..., soi="APOBEC") # APOBEC_mva; P-0004246-T01-IM5 57 SNVs 0.99998 high sg2.run(..., tumor_type="prost", seq_type="op", soi="SBS3") # ValueError: EXPERIMENTAL ... force=True sg2.run(..., soi="HRD") # nogis variant; 50 rows; hrd_call HRP/HRD-low/HRD/MMRD sg2.run(..., soi="HRD", segments="panels.seg") # .seg -> tcn variant (fga, lst_lr) sg2.predict_mmrd_ppd(input=MAF, ref_genome="hg19", tumor_type="breast", seq_type="msk") # MMRD_category: P-0007087-T01-IM5 MMRD (MMRD_prob 0.999); others MMRP Exome HRD on six TCGA breast tumors with ASCAT segments (seq_type tcga_mc3): hrd_variant gis; hrd_group breast; three HRD (gis 64–70, sig3_score ~0.9–0.98), three HRP (gis 0–3); SBS3 classifier alone gave TCGA-AO-A0J2 (82 SNVs) only a loose call where HRD called HRD with high confidence. Training smoke run: 40 PCAWG breast genomes (VCF folder; samples named by file stem), msk only, annotate gauss -> 15 pos / 25 neg (P0=25 P60=9 P100=6); simulate -> median 14 SNVs, 7 below floor; tune monologit cross_fit 2 repeats -> card evidence "weak", gate_reason "not certified: 19 distinct negatives bound FPR at 0.158 > 0.1 (30 needed)"; export -> store; sg2.model(name, root_dir) -> run(model=m) works. ## Known limits (alpha.20) VCF FILTER ignored for SNVs; no custom seq_type names; auto cannot split msk/fo/op; repeat loci, msk HRD grid, bundled BEDs are hg19; Windows needs the MS C++ Build Tools and a plain-text FASTA; readjust has no minimum batch; tune_hrd CV only; supplementation usually harmful; Add JSON has no schema version; private repository (404 for outsiders, fetch needs gh login / GITHUB_TOKEN); HRD ~1 s per tumor on one worker (pass mxw="max"). ## sigscape studio SigMA2 runs as the real package on the local runner (sigscape-daemon). Steps: Build (annotate + cluster), Simulate, Tune on a VCF folder + exposure table in the runner's data/ folder; Tune alone on an Add JSON; Score (run()) on a VCF, VCF folder, MAF or SBS-96 spectra, with segments for HRD and predict_mmrd_ppd on request. Score passes snv_ctf and catalog only when set, so the classifier's own apply; score.json records catalog/catalogSource and snvFloor/snvFloorSource. Hooks: [data-studio="sigma2"][data-stage], [data-studio="sigma2-score"] (data-ready data-samples data-called data-model data-model-status), [data-studio="sigma2-score-table"] rows (data-sample data-score data-call data-below-floor), [data-studio="sigma2-score-summary"] (data-scored data-below-floor data-floor data-catalog), [data-studio="sigma2-model-card"] (data-status), [data-studio="sigma2-score-models"] (data-count), sigma2-score-export, sigma2-score-bridge. window.sigscapeStudio.openTool("sigma2"). Run files: scores.tsv score.json model_card.json config.json recipe.json. Docs: https://sigscape.org/docs/studio/sigma2 and https://sigscape.org/llms.txt