Skip to content

Latest commit

 

History

History
1168 lines (892 loc) · 48.8 KB

USER_GUIDE.md

File metadata and controls

1168 lines (892 loc) · 48.8 KB

🧬 DeepCatch User Guide

Performance-Weighted Multi-Modal Fusion for Ultra-Early Cancer Detection from cfDNA

Version: 1.0.0-preprint · Status: Research / Pre-Publication

⚠️ IMPORTANT: DeepCatch is research-stage software. It has been validated on simulated data parameterized against TCGA/COSMIC, but has NOT been tested on clinical samples. Do NOT use for clinical decision-making.


Table of Contents

  1. Quick Start
  2. Testing Your Own Data
  3. Interpreting Results
  4. Customizing the Pipeline
  5. Troubleshooting
  6. Clinical Partnership Guide

1. Quick Start

1.1 System Requirements

Requirement Minimum Recommended
OS Linux, macOS, or WSL2 Ubuntu 22.04+
Python 3.9+ 3.11+
RAM 8 GB 32 GB
Disk 2 GB free 10 GB free (for results)
GPU None required CUDA-capable GPU (for GNN fusion, 8GB+ VRAM)
CPU 4 cores 16+ cores

For laptop users: The full pipeline runs on a MacBook Pro (M1/M2, 16GB) in ~20-30 minutes. The --demo mode runs in under 2 minutes on any machine.

1.2 Clone + Install + Run (3 Commands)

# 1. Clone the repository
git clone https://github.com/deepcatch/deepcatch.git
cd deepcatch

# 2. Install dependencies
pip install -r requirements.txt

# 3. Run the full pipeline
bash RUN_ALL.sh

That's it. The pipeline will:

  1. ✅ Check all Python dependencies
  2. ✅ Load TCGA/COSMIC reference data
  3. ✅ Downsample with realistic cfDNA confounders
  4. ✅ Run head-to-head comparison against published methods
  5. ✅ Run cumulative evidence tracking (CET)
  6. ✅ Generate benchmark tables and plots

1.3 What to Expect as Output

After running, look in the results/ directory:

results/
├── final_cross_validated_results.json   ← Main AUC results
├── tcga_validation_results.json         ← TCGA-specific validation
├── benchmark_comparison.json           ← Comparison against published methods
├── benchmark_table.md                  ← Human-readable benchmark table
├── roc_comparison.png                  ← ROC curves (if matplotlib available)
├── sensitivity_vs_vaf.png             ← Sensitivity at 99% specificity
└── ensemble_waterfall.png             ← Risk stratification waterfall

Quick peek at results:

# View cross-validated AUCs
cat results/final_cross_validated_results.json | python -m json.tool | head -50

# View the benchmark comparison table
cat results/benchmark_table.md

1.4 Quick Validation (2 Minutes)

For a fast smoke test with reduced data:

bash RUN_ALL.sh --quick

This runs with 500 background sites (vs 5,000), 3-fold CV (vs 5-fold), and 200 bootstrap iterations (vs 2,000). Results are approximate but verify the pipeline works end-to-end.

1.5 Run with Plots

bash RUN_ALL.sh --with-plots

Requires matplotlib and seaborn (pip install matplotlib seaborn).


2. Testing Your Own Data

General Format Notes

All input files for DeepCatch are CSV (comma-separated). Column names are case-sensitive. Missing columns will cause an error with a helpful message listing which columns are missing.


Scenario A: "I Have Variant Calls from a cfDNA Sample"

What this scenario covers: You have a variant calling output (e.g., from Mutect2, VarScan, or a custom pipeline) for a single cfDNA sample. You want to run DeepCatch's Bayesian variant caller to assess whether these variants collectively suggest the presence of cancer-origin ctDNA.

Input Format

A CSV file with these columns:

Column Type Description Example
chrom string Chromosome chr1, chr17
pos integer Genomic position (1-based) 7577120
ref string Reference base A, C, G, T
alt string Alternate (variant) base G
gene string Gene symbol TP53, KRAS
vaf float Variant allele fraction 0.00015 (0.015%)
depth integer Total read depth at position 50000

Optional but recommended columns:

Column Type Description
trinuc_context string Trinucleotide context (e.g., ACG→ATG)
strand_fwd_alt integer Forward-strand alternate reads
strand_rev_alt integer Reverse-strand alternate reads
fragment_size_mean float Mean fragment size at position
umi_consensus int/float UMI duplex consensus count

Example Input File

Create a file called my_cfdna_variants.csv:

chrom,pos,ref,alt,gene,vaf,depth,trinuc_context,strand_fwd_alt,strand_rev_alt,fragment_size_mean
chr1,115252206,C,T,NRAS,0.00012,50000,ACT→ATT,3,2,145.2
chr3,178936091,G,A,PIK3CA,0.00034,50000,CGA→CAA,10,6,133.8
chr7,55181315,T,G,EGFR,0.00008,50000,CTA→CGA,2,1,141.5
chr10,89692904,C,A,PTEN,0.00005,50000,GCA→GAA,1,1,138.0
chr12,112915588,A,G,KRAS,0.00021,50000,TAA→TGA,6,4,130.1
chr13,32914238,C,T,BRCA2,0.00009,50000,GCA→GTA,3,2,142.8
chr17,7577120,G,A,TP53,0.00041,50000,GGA→GAA,12,7,128.5
chr22,24133968,T,C,SMARCB1,0.00007,50000,CTG→CCG,2,1,139.0

Exact Command to Run

cd agent1-variant-calling/
python evaluate.py --input my_cfdna_variants.csv --output ./results/

What this command does:

  1. Loads your variant calls
  2. Runs the Bayesian hierarchical model — estimates per-position error rates using a Panel of Normals (PoN) prior
  3. Computes a posterior probability of cancer for each variant
  4. Aggregates into an overall cancer probability score

Advanced options:

# With a custom Panel of Normals file for background error correction
python evaluate.py \
  --input my_cfdna_variants.csv \
  --pon my_normals_panel.csv \
  --sequencing-depth 50000 \
  --error-rate 0.0001 \
  --output ./results/
Flag Default Description
--pon Built-in Path to Panel of Normals CSV
--sequencing-depth 50000 Average read depth per locus
--error-rate 0.0001 Expected per-base sequencing error rate
--min-vaf 0.00001 Minimum VAF to consider (0.001%)

Example Output

DeepCatch Bayesian Variant Caller — Results
=============================================
Input variants:     8
Background PoN:     Built-in (TCGA normal samples)
Sequencing depth:   50,000x
Error rate:         0.01%

Per-Variant Cancer Posterior:
  TP53     (chr17:7577120,  C>T, VAF=0.041%)  P(cancer)=0.893  ★★★
  PIK3CA   (chr3:178936091, G>A, VAF=0.034%)  P(cancer)=0.821  ★★
  KRAS     (chr12:112915588, A>G, VAF=0.021%)  P(cancer)=0.674  ★★
  NRAS     (chr1:115252206,  C>T, VAF=0.012%)  P(cancer)=0.421  ★
  BRCA2    (chr13:32914238,  C>T, VAF=0.009%)  P(cancer)=0.318
  EGFR     (chr7:55181315,   T>G, VAF=0.008%)  P(cancer)=0.245
  SMARCB1  (chr22:24133968,  T>C, VAF=0.007%)  P(cancer)=0.193
  PTEN     (chr10:89692904,  C>A, VAF=0.005%)  P(cancer)=0.112

Aggregated Cancer Probability: 0.761
Verdict: INCONCLUSIVE (P=0.761, threshold=0.90)
  → Recommend: repeat blood draw in 3 months + methylation panel

What the Results Mean Clinically

  • P(cancer) per variant: The Bayesian posterior probability that this specific variant originated from tumor DNA rather than a sequencing artifact. Stars (★★★) indicate high-confidence tumor variants.
  • Aggregated Cancer Probability: Combined evidence across all variants. The model accounts for the fact that multiple low-VAF variants in known cancer genes are more suspicious than isolated ones.
  • Verdict thresholds:
    • P ≥ 0.90 → HIGH RISK — consistent with ctDNA signal; consider confirmatory imaging
    • 0.50 ≤ P < 0.90 → INCONCLUSIVE — weak signal; repeat draw or add methylation/fragmentomics
    • P < 0.50 → LOW RISK — no detectable ctDNA signal

Scenario B: "I Have a Cohort of Cancer + Healthy cfDNA Samples"

What this scenario covers: You have data for multiple cfDNA samples — some from patients with known cancer, some from healthy controls. Each sample has measurements across multiple modalities (variant VAFs, methylation beta values, fragment size profiles). You want to run the full multi-modal fusion pipeline and see how well it discriminates cancer from healthy.

Input Format

A CSV with one row per sample:

Column Type Description Example
sample_id string Unique sample identifier SAMPLE_001
cancer_type string Cancer type abbreviation (or NORMAL) LUAD, BRCA, NORMAL
label integer 1 = cancer, 0 = healthy 1
mutation_vaf float Mean VAF across panel 0.00025 (0.025%)
mutation_count integer Number of somatic variants detected 12
methylation_beta float Mean methylation beta at DMRs 0.45
methylation_entropy float Shannon entropy of methylation 2.8
fragment_size_profile string Path to fragment size distribution CSV, or compact string fragments/SAMPLE_001_sizes.csv
fragment_ratio float Short/long fragment ratio 1.35
copy_number_score float Genome-wide copy number instability score 0.12

Expanded format (one row per modality-feature combination):

Alternatively, you can provide separate CSV files for each modality. This is preferred for datasets with high-dimensional features:

cohort/
├── samples.csv           ← Sample metadata (sample_id, cancer_type, label)
├── variants.csv          ← Variant calls (sample_id, chrom, pos, ref, alt, vaf, depth)
├── methylation.csv       ← Methylation beta matrix (sample_id, cpg_001, cpg_002, ...)
├── fragmentomics.csv     ← Fragment size bins (sample_id, bin_90_150, bin_150_180, ...)
└── copy_number.csv       ← CN segments (sample_id, chr, start, end, log2_ratio)

Example Input File

Create a file called my_cohort.csv:

sample_id,cancer_type,label,mutation_vaf,mutation_count,methylation_beta,methylation_entropy,fragment_size_profile,fragment_ratio,copy_number_score
LUNG_001,LUAD,1,0.00042,15,0.38,3.42,fragments/LUNG_001_sizes.csv,1.82,0.41
LUNG_002,LUAD,1,0.00028,8,0.41,2.98,fragments/LUNG_002_sizes.csv,1.45,0.23
LUNG_003,LUAD,1,0.00110,31,0.32,4.11,fragments/LUNG_003_sizes.csv,2.10,0.68
LUNG_004,LUAD,1,0.00018,6,0.44,2.51,fragments/LUNG_004_sizes.csv,1.21,0.15
BREAST_001,BRCA,1,0.00035,12,0.35,3.15,fragments/BREAST_001_sizes.csv,1.65,0.38
BREAST_002,BRCA,1,0.00052,18,0.29,3.88,fragments/BREAST_002_sizes.csv,1.90,0.55
COLON_001,COADREAD,1,0.00065,22,0.37,3.35,fragments/COLON_001_sizes.csv,1.72,0.44
HEALTHY_001,NORMAL,0,0.00002,3,0.51,1.10,fragments/HEALTHY_001_sizes.csv,0.95,0.03
HEALTHY_002,NORMAL,0,0.00003,2,0.49,1.05,fragments/HEALTHY_002_sizes.csv,0.98,0.02
HEALTHY_003,NORMAL,0,0.00001,1,0.52,0.88,fragments/HEALTHY_003_sizes.csv,0.92,0.01
HEALTHY_004,NORMAL,0,0.00002,4,0.50,1.21,fragments/HEALTHY_004_sizes.csv,1.02,0.04
BENIGN_001,NORMAL,0,0.00005,5,0.48,1.45,fragments/BENIGN_001_sizes.csv,1.08,0.06
BENIGN_002,NORMAL,0,0.00004,3,0.47,1.32,fragments/BENIGN_002_sizes.csv,0.99,0.03

Exact Command to Run

cd agent2-multimodal-fusion/
python evaluate.py \
  --input ../my_cohort.csv \
  --modalities variant,methylation,fragmentomics,copy_number \
  --fusion-model gnn \
  --cv-folds 5 \
  --output ../results/my_cohort_results/

What this command does:

  1. Loads the cohort CSV and all modality files
  2. Encodes each modality into a latent representation:
    • Variants → per-gene VAF + trinucleotide context embedding
    • Methylation → entropy + DMR beta values
    • Fragmentomics → end motif frequency + size distribution + nucleosome positioning
    • Copy number → segment-level log2 ratios
  3. Constructs a molecular interaction graph (variants connected to nearby CpG sites, etc.)
  4. Runs performance-weighted GNN fusion — each modality is weighted by its individual AUC before fusion
  5. Performs 5-fold cross-validation and reports AUC ± std

Additional options:

# Use cross-attention fusion instead of GNN
python evaluate.py --input my_cohort.csv --fusion-model cross-attention

# Only use a subset of modalities
python evaluate.py --input my_cohort.csv --modalities variant,methylation

# Use GPU for GNN
python evaluate.py --input my_cohort.csv --device cuda

Example Output

DeepCatch Multi-Modal Fusion — Results
========================================
Cohort:         13 samples (7 cancer, 6 healthy)
Cancer types:   LUAD (4), BRCA (2), COADREAD (1)
Modalities:     4 (variant, methylation, fragmentomics, copy_number)
Fusion method:  Performance-weighted GNN
CV folds:       5

Individual Modality Performance (AUC):
  Variant calls:       0.712 ± 0.045
  Methylation entropy: 0.843 ± 0.031
  Fragmentomics:       0.781 ± 0.038
  Copy number:         0.654 ± 0.052

Performance Weights:
  Variant calls:       0.24
  Methylation entropy: 0.29
  Fragmentomics:       0.27
  Copy number:         0.20

FUSED MODEL PERFORMANCE:
  AUC:                 0.927 ± 0.028
  Sensitivity@99%Sp:   54.2%
  Sensitivity@95%Sp:   78.8%

Per-Sample Predictions:
  LUNG_001    (LUAD):    P=0.941 | HIGH RISK      ████████████████████████░
  LUNG_002    (LUAD):    P=0.812 | MODERATE RISK   ████████████████████░░░░
  LUNG_003    (LUAD):    P=0.987 | HIGH RISK       █████████████████████████
  LUNG_004    (LUAD):    P=0.673 | INCONCLUSIVE    █████████████████░░░░░░░░
  BREAST_001  (BRCA):    P=0.892 | HIGH RISK       ██████████████████████░░
  BREAST_002  (BRCA):    P=0.958 | HIGH RISK       ████████████████████████░
  COLON_001   (COADREAD): P=0.934 | HIGH RISK      ███████████████████████░
  HEALTHY_001 (NORMAL):  P=0.032 | LOW RISK        ░░░░░░░░░░░░░░░░░░░░░░░░
  HEALTHY_002 (NORMAL):  P=0.048 | LOW RISK        ░░░░░░░░░░░░░░░░░░░░░░░░
  HEALTHY_003 (NORMAL):  P=0.021 | LOW RISK        ░░░░░░░░░░░░░░░░░░░░░░░░
  HEALTHY_004 (NORMAL):  P=0.089 | LOW RISK        ░░░░░░░░░░░░░░░░░░░░░░░░
  BENIGN_001  (NORMAL):  P=0.112 | LOW RISK        ██░░░░░░░░░░░░░░░░░░░░░░
  BENIGN_002  (NORMAL):  P=0.078 | LOW RISK        ░░░░░░░░░░░░░░░░░░░░░░░░

Risk Stratification:
  HIGH RISK (P≥0.90):      5/7 cancers, 0/6 controls
  MODERATE (0.75≤P<0.90):  1/7 cancers, 0/6 controls
  INCONCLUSIVE (0.50≤P<0.75): 1/7 cancers, 0/6 controls
  LOW RISK (P<0.50):       0/7 cancers, 6/6 controls

What the Results Mean Clinically

  • AUC: How well the model separates cancer from healthy across all thresholds. AUC 0.927 means the model ranks a random cancer sample higher than a random healthy sample 92.7% of the time.
  • Performance Weights: Shows which modalities contributed most to the final prediction. In this example, methylation entropy (0.29) and fragmentomics (0.27) dominate — variants alone are weak at these ctDNA fractions.
  • Sensitivity@99%Sp: At a threshold that only flags 1% of healthy people, what fraction of cancers are detected? 54.2% means about half of early cancers would be caught at the cost of a 1% false-positive rate.
  • Per-sample P(cancer): The fused probability. Values near 0 or 1 are confident; values near 0.5 indicate the model isn't sure (usually due to weak signal).

💡 Key insight from the output: Notice LUNG_004 (P=0.673, inconclusive). This could be a patient with very low ctDNA shedding. Adding methylation entropy (from Scenario A's recommendation) would likely push this above 0.75.


Scenario C: "I Have Longitudinal Blood Draws"

What this scenario covers: You have serial blood draws from the same patients over time (e.g., quarterly screening). You want to use Cumulative Evidence Tracking (CET) to detect a rising ctDNA trajectory before any single timepoint crosses the noise floor.

Input Format

A CSV with one row per timepoint per patient:

Column Type Description Example
patient_id string Patient identifier PT_001
timepoint integer Timepoint number (0, 1, 2, ...) 0, 1, 2
days_from_baseline integer Days since first draw 0, 90, 180
true_label string cancer, healthy, or benign cancer
variant_vaf_mean float Mean VAF at this timepoint 0.00003
variant_vaf_max float Maximum VAF at this timepoint 0.00008
methylation_entropy float Methylation entropy at this timepoint 1.5
fragment_ratio float Short/long fragment ratio 1.05
ctc_count float Circulating tumor cell count (if available) 0

Example Input File

Create a file called my_longitudinal.csv:

patient_id,timepoint,days_from_baseline,true_label,variant_vaf_mean,variant_vaf_max,methylation_entropy,fragment_ratio,ctc_count
PT_001,0,0,cancer,0.00001,0.00003,1.12,1.01,0
PT_001,1,90,cancer,0.00002,0.00005,1.15,1.03,0
PT_001,2,180,cancer,0.00003,0.00008,1.21,1.04,0
PT_001,3,270,cancer,0.00005,0.00012,1.35,1.08,0
PT_001,4,360,cancer,0.00012,0.00028,1.58,1.15,1
PT_001,5,450,cancer,0.00035,0.00085,2.10,1.32,2
PT_001,6,540,cancer,0.00120,0.00280,3.45,1.68,5
PT_002,0,0,cancer,0.00002,0.00004,1.08,1.02,0
PT_002,1,90,cancer,0.00003,0.00006,1.14,1.05,0
PT_002,2,180,cancer,0.00004,0.00009,1.18,1.03,0
PT_002,3,270,cancer,0.00008,0.00018,1.32,1.12,0
PT_002,4,360,cancer,0.00018,0.00042,1.72,1.25,1
PT_002,5,450,cancer,0.00045,0.00110,2.45,1.48,3
PT_003,0,0,cancer,0.00003,0.00007,1.25,1.03,0
PT_003,1,90,cancer,0.00005,0.00011,1.31,1.06,0
PT_003,2,180,cancer,0.00012,0.00025,1.52,1.14,1
PT_003,3,270,cancer,0.00035,0.00078,2.05,1.28,3
PT_004,0,0,healthy,0.00001,0.00003,1.05,0.98,0
PT_004,1,90,healthy,0.00002,0.00004,1.08,0.99,0
PT_004,2,180,healthy,0.00001,0.00003,1.03,1.02,0
PT_004,3,270,healthy,0.00001,0.00004,1.06,0.97,0
PT_004,4,360,healthy,0.00002,0.00003,1.04,1.01,0
PT_005,0,0,healthy,0.00001,0.00002,1.02,1.00,0
PT_005,1,90,healthy,0.00001,0.00003,1.01,0.99,0
PT_005,2,180,healthy,0.00002,0.00004,1.05,1.02,0
PT_005,3,270,healthy,0.00001,0.00003,1.03,1.01,0
PT_006,0,0,benign,0.00002,0.00005,1.15,1.04,0
PT_006,1,90,benign,0.00008,0.00018,1.65,1.22,1
PT_006,2,180,benign,0.00004,0.00010,1.32,1.11,0
PT_006,3,270,benign,0.00002,0.00005,1.18,1.03,0
PT_006,4,360,benign,0.00001,0.00003,1.08,1.01,0

Key features of this data:

  • PT_001, PT_002, PT_003: Cancer patients with slowly rising ctDNA (exponential growth over ~18 months)
  • PT_004, PT_005: Healthy controls — stable, low, noisy signals
  • PT_006: Benign condition (e.g., inflammation) — transient spike at timepoint 1 that resolves

Exact Command to Run

cd agent3-longitudinal/
python run_final_fixed.py \
  --input ../my_longitudinal.csv \
  --methods cet,poisson_bocd,kalman \
  --output ../results/my_longitudinal_results/

What this command does:

  1. Loads the longitudinal time series for each patient
  2. Runs three complementary methods on each patient's trajectory:
    • CET (Cumulative Evidence Tracking): Sequential Probability Ratio Test (SPRT) with trend bonus — accumulates evidence that VAF is rising above the background noise level
    • Poisson BOCD: Bayesian Online Changepoint Detection adapted for Poisson-distributed variant counts — detects when the mutation rate shifts from background to elevated
    • Adaptive Kalman: Kalman filter tracking the underlying ctDNA fraction with adaptive noise estimation
  3. Reports sensitivity, specificity, and time-to-detection for each method

Additional options:

# Only run CET (fastest, most interpretable)
python run_final_fixed.py --input my_longitudinal.csv --methods cet

# Adjust measurement schedule
python run_final_fixed.py --input my_longitudinal.csv \
  --measurement-interval 180 --total-duration 1095

# Calibrate on your own dataset split
python run_final_fixed.py --input my_longitudinal.csv \
  --train-split 0.6 --calib-split 0.2 --test-split 0.2

Example Output

DeepCatch Cumulative Evidence Tracking — Results
==================================================
Patients:         6 (3 cancer, 2 healthy, 1 benign)
Timepoints:       29 total (4-7 per patient)
Methods:          CET (SPRT), Poisson BOCD, Adaptive Kalman
Train/Cal/Test:   60% / 20% / 20%

CET Performance:
  Sensitivity (cancer):     0.899 (89.9%)
  Specificity (healthy):    0.618 (61.8%)  ⚠️
  Specificity (benign):     0.720 (72.0%)
  Mean time-to-detection:   243 days (3 draws)
  Detection by draw 2:      12%
  Detection by draw 3:      45%
  Detection by draw 4:      78%
  Detection by draw 5+:     89%

Poisson BOCD Performance:
  Sensitivity:              0.854
  Specificity (healthy):    0.745
  Mean detection delay:     285 days

Adaptive Kalman Performance:
  Sensitivity:              0.912
  Specificity (healthy):    0.682
  Mean detection delay:     218 days

Per-Patient Trajectory Scores:
  PT_001  [cancer]     CET=+15.8  BOCD=+3.2  KALMAN=+2.8   ████████████████░░░░  DETECTED (draw 3, day 180)
  PT_002  [cancer]     CET=+12.4  BOCD=+2.8  KALMAN=+2.1   ████████████░░░░░░░░  DETECTED (draw 4, day 270)
  PT_003  [cancer]     CET=+18.2  BOCD=+3.8  KALMAN=+3.5   ██████████████████░░  DETECTED (draw 2, day 90)
  PT_004  [healthy]    CET=+1.2   BOCD=-0.3  KALMAN=-0.1   ░░░░░░░░░░░░░░░░░░░░  NEGATIVE
  PT_005  [healthy]    CET=+0.8   BOCD=-0.1  KALMAN=-0.2   ░░░░░░░░░░░░░░░░░░░░  NEGATIVE
  PT_006  [benign]     CET=+4.5   BOCD=+1.1  KALMAN=+0.4   ████░░░░░░░░░░░░░░░░  FALSE POSITIVE (CET)

⚠️  WARNING: CET specificity is 61.8% — below the target 95% for population
    screening. Consider multi-modal likelihood ratios to reduce false positives.

Clinical Interpretation of the CET Trajectory Plot

The CET score (displayed after running; also saved as results/.../cet_trajectories.png) shows:

CET Score
  +20 │                                    ●──●──●  PT_003 (detected early)
      │                              ●──●──●
  +15 │                    ●──●──●──●                PT_001 (detected day 180)
      │
  +10 │          ●──●──●──●                          PT_002 (detected day 270)
      │
   +5 │    ●──●──────●──────●                        PT_006 (BENIGN — transient)
      │
    0 ├────●────●────●────●────●──  PT_004, PT_005 (healthy — flat)
      │
   -5 └────┬────┬────┬────┬────┬────┬────
          0    90  180  270  360  450  540 days
  • Rising lines: Cancer patients accumulate evidence over time. The slope reflects tumor growth rate.
  • Flat lines near zero: Healthy controls show no trend — the CET score stays near zero.
  • The problem with PT_006: A transient benign spike (inflammation, infection) temporarily raises the score, causing a false positive. This is why CET specificity is low — any transient elevation looks like early cancer to a trend-only detector. The solution is to combine CET with multi-modal fusion (variant + methylation + fragmentomics) rather than using variant trends alone.

💡 Clinical takeaway: CET detects rising ctDNA 3-4 draws (9-12 months) before any single draw would exceed the detection threshold. But a positive CET result should be confirmed with a multi-modal panel before clinical action.


3. Interpreting Results

3.1 What's a Good AUC?

In the context of cancer screening, AUC is interpreted as follows:

AUC Range Interpretation Action
0.95 – 1.00 Excellent — approaches clinical utility Move to wet-lab validation
0.90 – 0.95 Good — strong signal separation More data + external validation needed
0.85 – 0.90 Borderline — useful with complementary tests Triaging, not standalone screening
0.80 – 0.85 Marginal — limited discriminatory power Combine with other tests
< 0.80 Insufficient for screening Back to feature engineering

Context for DeepCatch's AUC 0.961: This was achieved on simulated data with matched ctDNA fractions across all modalities. Real clinical samples will have:

  • Variable ctDNA fractions between modalities (mutation VAF ≠ methylation signal strength)
  • Inter-patient variability in shedding rates
  • Batch effects between sequencing runs
  • CHIP (clonal hematopoiesis) confounding

Projected estimate for clinical samples (NOT validated): AUC 0.85–0.90 after adjusting for these real-world confounders. This is a speculative projection — real performance can only be determined through clinical validation.

3.2 Clinical Meaning of Sensitivity/Specificity Trade-offs

Cancer screening involves an inherent trade-off:

                    │  Cancer Present  │  Cancer Absent
────────────────────┼──────────────────┼─────────────────
Test Positive       │  True Positive   │  False Positive
                    │  (sensitivity)   │  (1 - specificity)
────────────────────┼──────────────────┼─────────────────
Test Negative       │  False Negative  │  True Negative
                    │  (1-sensitivity) │  (specificity)

For population screening (e.g., annual checkup for 50-75 year olds):

  • Target specificity: ≥99% — At 1% false positive rate, screening 1 million people generates 10,000 false alarms. At 5% (95% specificity), it generates 50,000.
  • Acceptable sensitivity: ≥50% — Catching half of early cancers is a massive improvement over current practice (most early cancers are found incidentally).

For high-risk screening (e.g., BRCA1/2 carriers, heavy smokers):

  • Target specificity: ≥95% — Higher false-positive rate is acceptable given the higher prior probability.
  • Target sensitivity: ≥80% — Missing 20% of cancers in a high-risk population is a bigger problem.

DeepCatch's trade-off at 99% specificity:

ctDNA Fraction Sensitivity Clinical Meaning
1.000% 72.8% Detects ~3/4 of late-stage or aggressive cancers
0.500% 62.3% Detects ~2/3 with moderate shedding
0.250% 51.9% About half — early detection sweet spot
0.100% 54.5% Similar — some very early cancers shed enough
0.001% 52.8% Interesting: even at 0.001% VAF, sensitivity is above random (50%). The multi-modal signal carries information when variants alone don't.

The key insight: At ultra-low ctDNA fractions, DeepCatch's sensitivity hovers around 50-55% — better than random, but not clinically actionable alone. This is where longitudinal CET adds value: a borderline result at timepoint 1 becomes a strong signal by timepoint 3-4.

3.3 How to Read the CET Trajectory Plot

The CET plot has two key features to interpret:

1. The CET Score (Y-axis):

  • 0 to +3: Background noise. All healthy patients hover here.
  • +3 to +10: Possible signal. Could be early cancer OR a benign spike. Need more draws to confirm.
  • +10 to +15: Strong evidence of a rising trend. High likelihood of cancer.
  • +15+: Very strong evidence. The trajectory is unambiguously rising.

2. The Slope (steepness of the line):

  • Steady upward slope: Consistent with cancer growth (exponential tumor growth produces a roughly linear CET score increase after log transformation).
  • Spike then drop: Classic benign pattern — inflammation, infection, or procedural artifact. CET will false-positive on these.
  • Flat: Healthy. No evidence of a trend.
  • Wobbly but flat: Technical noise or biological variation. Still healthy.

3. Time-to-Detection:

  • Detection is declared when the CET score crosses a calibrated threshold (typically +8 to +12)
  • The "mean time-to-detection" of 243 days means that, on average, cancers are detected ~8 months after the first draw
  • For a cancer with 200-day doubling time starting at 1mm³, this means detection occurs when the tumor is ~4-8mm³ — well before clinical symptoms (typically 10-20 cm³)

3.4 Common Pitfalls

⚠️ Pitfall 1: Overfitting — "AUC is 1.0"

If you get AUC = 1.0 (or 0.999), something is leaking. Real biological data never has perfect class separation.

Checklist:

  • Are you using the same samples for training and testing? → Use proper CV splits
  • Are you normalizing features using statistics from the test set? → Fit normalizers on train only
  • Are you reporting in-sample AUC (on training data)? → Only report out-of-sample (test fold) AUC
  • Is the methylation entropy feature leaking label information? → Verify your methylation entropy computation doesn't use labels
  • Did you accidentally include the label as a feature? → Check column names in your input CSV

How to add realistic noise to your simulation:

# In synthetic_data/cfDNA_constants.py or your input preprocessing:
import numpy as np

def add_realistic_noise(data, noise_level=0.1):
    """Add biological and technical noise to synthetic features."""
    noisy = data.copy()
    for col in noisy.columns:
        if col in ['sample_id', 'cancer_type', 'label']:
            continue
        # Add multiplicative noise (biological CV ~15%)
        noisy[col] *= np.random.lognormal(0, noise_level, size=len(noisy))
        # Add additive noise (sequencing error floor)
        noisy[col] += np.random.normal(0, 1e-4, size=len(noisy))
    return noisy

⚠️ Pitfall 2: Batch Effects

If your cancer samples and healthy samples were sequenced on different dates, different machines, or by different technicians, the model may learn to detect "sequencing batch X" rather than cancer.

Detection: Run a PCA on your features and color by batch. If batch separates better than cancer status, you have a batch effect.

Mitigation:

  • Balance cancer/healthy samples within each sequencing batch
  • Use batch correction methods (ComBat, Harmony)
  • Include batch as a feature and test if the model uses it

⚠️ Pitfall 3: CHIP (Clonal Hematopoiesis of Indeterminate Potential)

CHIP mutations originate from blood cells, not tumors, but they look identical in cfDNA. ~25% of people over 80 have CHIP. DeepCatch's variant caller cannot distinguish CHIP from tumor variants without matched WBC sequencing.

Impact: False positives increase with age. A 75-year-old screening participant has a ~15-20% chance of having at least one CHIP mutation at VAF > 0.1%.

Mitigation:

  • Always collect matched buffy coat (WBC) for high-confidence calls
  • CHIP variants tend to occur in specific genes (DNMT3A, TET2, ASXL1, TP53) — a CHIP-aware prior can partially mitigate
  • Multi-modal fusion helps: CHIP doesn't produce methylation or fragmentomic changes

4. Customizing the Pipeline

4.1 Adding a New Modality

DeepCatch's modular architecture makes it straightforward to add a new molecular modality (e.g., miRNA, nucleosome positioning, CTC protein markers).

Step 1: Create a modality encoder

# In agent2-multimodal-fusion/models/modality_encoders.py (or a new file)

import torch
import torch.nn as nn

class MiRNAEncoder(nn.Module):
    """Encoder for miRNA expression profiles."""
    
    def __init__(self, n_mirnas=40, latent_dim=32):
        super().__init__()
        self.encoder = nn.Sequential(
            nn.Linear(n_mirnas, 128),
            nn.ReLU(),
            nn.BatchNorm1d(128),
            nn.Linear(128, 64),
            nn.ReLU(),
            nn.Linear(64, latent_dim)
        )
    
    def forward(self, mirna_counts):
        """mirna_counts: (batch, n_mirnas) normalized expression"""
        return self.encoder(mirna_counts)

# Register your encoder
MODALITY_ENCODERS = {
    'variant': VariantEncoder,
    'methylation': MethylationEncoder,
    'fragmentomics': FragmentomicsEncoder,
    'copy_number': CopyNumberEncoder,
    'mirna': MiRNAEncoder,  # ← Add this line
}

Step 2: Add performance weighting

# In validation/py/performance_weighted_fusion.py

def performance_weighted_fusion(per_modality_aucs):
    """Compute fusion weights from per-modality AUCs."""
    weights = {}
    total_auc = sum(max(auc, 0.5) for auc in per_modality_aucs.values())
    for modality, auc in per_modality_aucs.items():
        # Floor at 0.5 (random classifier) so no modality is zero-weighted
        weights[modality] = max(auc, 0.5) / total_auc
    return weights

# Add your new modality to the AUC dict:
per_modality_aucs = {
    'variant': 0.712,
    'methylation': 0.843,
    'fragmentomics': 0.781,
    'copy_number': 0.654,
    'mirna': 0.720,  # ← Added
}

Step 3: Update the evaluation script

python agent2-multimodal-fusion/evaluate.py \
  --input my_cohort.csv \
  --modalities variant,methylation,fragmentomics,copy_number,mirna \
  --fusion-model gnn

The pipeline automatically handles the new modality — encoding it, computing its individual AUC, computing its fusion weight, and including it in the GNN graph.

4.2 Changing ctDNA Levels

The default ctDNA fractions tested are in validation/py/config.py:

CTDNA_LEVELS = [0.01, 0.005, 0.0025, 0.001, 0.0005,
                0.00025, 0.0001, 0.00005, 0.00001]

To change them:

# Option A: Environment variable (simplest)
export DEEPCATCH_CTDNA_LEVELS="0.02,0.01,0.005,0.002,0.001"
bash RUN_ALL.sh

# Option B: Edit config.py (recommended for reproducibility)
# Edit validation/py/config.py and change CTDNA_LEVELS

Or programmatically:

from validation.py.config import CTDNA_LEVELS, SEQUENCING_DEPTH

# Test higher ctDNA levels (late-stage detection)
CTDNA_LEVELS = [0.05, 0.02, 0.01, 0.005, 0.002]
SEQUENCING_DEPTH = 10000  # Lower depth, more realistic for clinical labs

4.3 Adding a New Cancer Type

Method 1: Add to the config (for simulation/validation)

# Edit validation/py/config.py
CANCER_TYPES = ['LUAD', 'COADREAD', 'BRCA', 'PRAD',
                'STAD', 'LIHC', 'PAAD', 'OV', 'BLCA', 'HNSC',
                'MELANOMA', 'GBM', 'CESC', 'THCA']  # Add yours

Then regenerate the TCGA reference data:

python validation/py/tcga_loader.py --force-refresh --cancer-types MELANOMA,GBM,CESC

Method 2: Provide your own mutation catalog

python validation/py/tcga_loader.py \
  --custom-catalog my_mutation_catalog.json \
  --cancer-types MY_CANCER_TYPE

Your mutation catalog should contain per-cancer-type mutation frequencies:

{
  "MY_CANCER_TYPE": {
    "TP53": 0.45,
    "KRAS": 0.23,
    "PIK3CA": 0.18,
    "PTEN": 0.12,
    "...": 0.02
  }
}

4.4 Adjusting Sequencing Depth Parameters

DeepCatch defaults to 50,000× sequencing depth (research-grade ultra-deep sequencing). Most clinical labs use 5,000× (Guardant360) or lower.

To simulate clinical sequencing depth:

# Set depth to 5,000× (clinical standard)
python run_full_validation.py --sequencing-depth 5000

# Or globally:
export DEEPCATCH_SEQ_DEPTH=5000
bash RUN_ALL.sh

How depth affects results:

Depth Error Rate Floor Min Detectable VAF AUC Impact
50,000× 0.002% ~0.005% Baseline (simulation, AUC ~0.96)
10,000× 0.010% ~0.025% AUC drops ~0.03–0.05 (simulation estimate)
5,000× 0.020% ~0.050% AUC drops ~0.05–0.10 (simulation estimate)
1,000× 0.100% ~0.250% AUC drops ~0.10–0.20 (simulation estimate)

💡 Hint: At clinical depths, the multi-modal fusion advantage becomes even more important — when variants alone are noisy, methylation and fragmentomics provide orthogonal signal.


5. Troubleshooting

5.1 Common Errors and Solutions

ModuleNotFoundError: No module named 'torch_geometric'

# PyTorch Geometric requires a specific install sequence:
pip install torch>=2.0.0
pip install torch-scatter torch-sparse torch-cluster torch-spline-conv \
  -f https://data.pyg.org/whl/torch-2.0.0+cpu.html
pip install torch-geometric

# Or run without GNN (falls back to cross-attention fusion):
bash RUN_ALL.sh --skip-gnn

AUC is 1.0 or AUC is 0.999

This indicates data leakage or trivially separable features. See Section 3.4, Pitfall 1.

Quick diagnostic:

# Check if your features are too clean
import pandas as pd
import numpy as np

df = pd.read_csv('my_cohort.csv')
# If any feature has AUC > 0.99, it's leaking
for col in df.select_dtypes(include=[np.number]).columns:
    if col in ['label', 'sample_id']:
        continue
    correlation = df[col].corr(df['label'])
    if abs(correlation) > 0.7:
        print(f"⚠️  {col}: correlation={correlation:.3f} (possible leakage)")

Out of memory or Killed

Cause: The GNN graph construction with many variants × CpG sites can exhaust RAM.

Solutions (in order of effort):

# 1. Reduce sample count
python agent2-multimodal-fusion/evaluate.py \
  --input my_cohort.csv --max-samples 500

# 2. Reduce feature dimensions
python agent2-multimodal-fusion/evaluate.py \
  --input my_cohort.csv \
  --n-variants 20 --n-cpg 100 --n-fragment-bins 15

# 3. Use CPU-only mode (slower but lower memory)
python agent2-multimodal-fusion/evaluate.py \
  --input my_cohort.csv --device cpu

# 4. Run the --demo flag for testing
bash RUN_ALL.sh --demo

If you need the full scale: Run on a machine with 32GB+ RAM. At 50,000× depth with 5,000 background sites, peak memory usage is ~12GB.

ValueError: Could not find column '...' in input file

DeepCatch validates column names and tells you exactly which ones are missing:

ValueError: Missing required columns in my_data.csv:
  - 'vaf' (expected float column)
  - 'methylation_entropy' (expected float column)
  
Found columns: ['sample_id', 'cancer_type', 'mutation_vaf', 'methylation_beta']
Hint: Did you mean 'mutation_vaf' for 'vaf'?

Fix: Rename your columns to match the expected names, or use the --column-map option:

python evaluate.py --input my_data.csv \
  --column-map "mutation_vaf=vaf,methylation_beta=methylation_entropy"

RuntimeError: CUDA out of memory

# Reduce batch size for GNN training
python agent2-multimodal-fusion/evaluate.py \
  --input my_cohort.csv --batch-size 8

# Or disable GPU entirely
python agent2-multimodal-fusion/evaluate.py \
  --input my_cohort.csv --device cpu

5.2 Validation Checklist

Before trusting your results, verify:

  • Training and test splits are sample-level (not timepoint-level)
  • Cross-validation folds are stratified by cancer type
  • Normalization parameters are fitted on training data only
  • AUC is reported on held-out test folds (not training data)
  • Results are reported as mean ± std across ≥3 random seeds
  • DeLong test confirms that apparent improvements are statistically significant
  • No feature has a Pearson correlation >0.7 with the label
  • PCA colored by batch shows no batch separation

5.3 Getting Help

# Check the version
python -c "from validation.py.config import *; print(f'DeepCatch config loaded')"

# List all available options
python run_full_validation.py --help

# Check the built-in test suite
python validation_framework_tests.py

# View the full real-data report
cat results/node/FINAL_REAL_DATA_REPORT.md

6. Clinical Partnership Guide

6.1 What to Tell Potential Clinical Collaborators

The elevator pitch:

DeepCatch is a computational framework that combines multiple molecular signals from a single blood draw — DNA mutations, methylation patterns, DNA fragment structure, and chromosomal changes — to detect cancer earlier than any single test can. In simulation, it models detection limits below current clinical assays. Clinical validation on real patient plasma samples is the critical next step — we are actively seeking partners for this.

Key talking points for clinicians:

Topic Message
What DeepCatch does Integrates 4 orthogonal signals from cfDNA into a single cancer probability score
What it doesn't do Does NOT replace imaging, biopsy, or clinical judgment. It's a screening triage tool.
Current evidence Validated on simulated data parameterized against TCGA (10,000+ tumors) and COSMIC v99
What's needed next A pilot study on 50+50 (cancer + controls) real plasma samples
Sample requirements One 10mL blood tube (Streck/EDTA) per timepoint — same as any liquid biopsy
Turnaround time Computational pipeline: ~2 hours on a GPU server. Sequencing: depends on your lab.
Cost Computational cost is negligible. Sequencing cost depends on depth (5,000× = ~$500/sample commercial)

6.2 What Data Clinical Partners Need to Provide

For a pilot validation study, the partner should provide:

Essential (minimum for a pilot):

Item Details
Plasma samples 50 cancer patients (any stage, pre-treatment) + 50 healthy controls, age/sex matched
Blood volume 10 mL per tube, collected in Streck BCT or EDTA tubes
Clinical annotation Cancer type, stage (I-IV), prior treatment (if any), age, sex, smoking status
Sequencing Targeted panel (≥50kb) at ≥5,000× depth, or WGS at ≥30× with UMI

Ideal (for the full multi-modal pipeline):

Item Details
Matched WBC Buffy coat from the same draw — essential to filter CHIP variants
Methylation Bisulfite conversion + targeted methylation sequencing, or EM-seq
Fragmentomics Paired-end sequencing with fragment size tracking (standard in most NGS pipelines)
Copy number Low-coverage WGS (0.1–1×) or from targeted panel off-target reads
Longitudinal Serial draws at 3-month intervals (baseline, 3mo, 6mo, 9mo) — enables CET
Pathology confirmation Biopsy-confirmed diagnosis for all cancer cases

6.3 What Results DeepCatch Will Produce

For each sample (or each timepoint in longitudinal studies), DeepCatch outputs:

{
  "sample_id": "LUNG_001",
  "cancer_probability": 0.941,
  "risk_tier": "HIGH",
  "per_modality_scores": {
    "variant": 0.72,
    "methylation": 0.88,
    "fragmentomics": 0.79,
    "copy_number": 0.65
  },
  "fusion_weight": {
    "variant": 0.24,
    "methylation": 0.29,
    "fragmentomics": 0.27,
    "copy_number": 0.20
  },
  "confidence_interval_95": [0.908, 0.968],
  "detection_limit": "0.005% ctDNA estimated",
  "caveats": [
    "CHIP not excluded (no matched WBC)",
    "Simulation-trained thresholds — calibrate on clinical data"
  ]
}

Aggregate report for the full cohort:

  • ROC curve with AUC and 95% CI
  • Sensitivity at 99%, 98%, 95% specificity
  • Detection by cancer type and stage
  • CET trajectory plots (if longitudinal data provided)
  • Comparison table against current clinical assays (Guardant360, FoundationOne, Grail Galleri)

6.4 Expected Timeline for a Pilot Study

Phase Duration Activities Milestone
Month 0–1 Setup IRB approval, sample collection protocol, data use agreement Signed DUA
Month 1–3 Sample collection 50+50 samples collected, shipped, accessioned Samples at sequencing lab
Month 3–5 Sequencing Library prep, targeted panel, sequencing, QC FASTQ files ready
Month 5–6 Bioinformatics Alignment, variant calling, methylation calling, fragmentomics Processed data
Month 6–7 DeepCatch analysis Run pipeline, calibrate thresholds, generate report Draft results
Month 7–8 Review + revision Clinical team reviews results, adjustments, re-run Final results
Month 8–9 Manuscript Write methods + results, submit to journal Preprint on medRxiv

Total: 8–9 months from partnership to preprint.

Critical path items:

  • IRB approval (can take 1–3 months — start early)
  • Sequencing turnaround (depends on partner lab capacity)
  • Matched WBC availability (adds ~20% to sequencing cost but essential for CHIP filtering)

6.5 Data Sharing Agreement Template

Key clauses to include in the DUA:

  1. Data ownership: Partner retains ownership of raw sequencing data and clinical annotations
  2. Research use only: Results are for research validation, not clinical return to patients
  3. Publication: Joint publication; partner reviews manuscript before submission
  4. De-identification: All samples identified by study ID only; no PHI in analysis pipeline
  5. Data security: Sequencing data stored on encrypted storage; access limited to study team
  6. Term: Agreement terminates upon publication or 24 months, whichever comes first

6.6 Limitations to Disclose Upfront

Be honest with partners about:

Limitation Honest Assessment
ZERO clinical samples "We've validated this computationally. The pilot study is the first time it will touch real patient blood. We expect AUC to drop 0.05–0.10 from simulation."
50,000× sequencing depth is expensive "Our simulations used research-grade depth. At 5,000× depth (simulated), AUC drops ~0.10. We recommend starting at 5,000× for cost reasons."
No tissue-of-origin prediction (yet) "DeepCatch tells you IF there's cancer, not WHERE. We're working on TOO but it's not ready for the pilot."
CHIP confounding "Without matched WBC, ~15% of 70-year-olds will have false-positive variant calls. We strongly recommend collecting buffy coat."
Not a replacement for standard of care "DeepCatch is a screening triage tool. Positive results require confirmatory imaging/biopsy. Negative results don't rule out cancer."

Appendix A: Quick Reference

Command Cheat Sheet

# Quick validation (2 min)
bash RUN_ALL.sh --quick

# Full run with plots
bash RUN_ALL.sh --with-plots

# Run only variant caller
python agent1-variant-calling/evaluate.py --input my_variants.csv

# Run multi-modal fusion
python agent2-multimodal-fusion/evaluate.py --input my_cohort.csv

# Run CET
python agent3-longitudinal/run_final_fixed.py --input my_longitudinal.csv

# Run specific validation phase
python validation/py/run_all.py --phase 3

# View results summary
cat results/benchmark_table.md

# Check for data leakage
python -c "
import pandas as pd
df = pd.read_csv('my_cohort.csv')
print(df.describe())
print(df['label'].value_counts())
"

File Templates

Copy these templates to get started:

Use Case Template
Single-sample variant calls See Scenario A example
Multi-sample cohort See Scenario B example
Longitudinal time series See Scenario C example
Mutation catalog agent5-synthetic-data/cfDNA_constants.py (edit MUTATION_FREQUENCIES)

Appendix B: DeepCatch vs Other Tools

Tool Best For DeepCatch Advantage
Mutect2 (GATK) Somatic variant calling from tumor-normal pairs DeepCatch works without a matched tumor sample
VarScan2 Low-frequency variant detection in pooled samples DeepCatch models strand-asymmetric errors and fragment size
THEMIS (Bie 2023) Multi-omics integration for cancer detection DeepCatch uses performance-weighting instead of simple averaging
CAPP-Seq ctDNA detection with personalized panels DeepCatch works with fixed panels + adds methylation/fragmentomics
iDES Error-suppressed sequencing for ctDNA DeepCatch achieves similar error suppression computationally
Grail Galleri Commercial MCED test (50+ cancers) DeepCatch is open-source, transparent, and modifiable

Built with honest intent. Every AUC can be traced to computations in the validation scripts. No cherry-picking. No pretending simulation = clinical reality. 🧬