Spaces:
Running
Running
| // Mock "compute" for the HydroPD UI (Milestone 1). Model metadata is REAL | |
| // (see data/models.ts + generated JSON); the per-peptide scores and training | |
| // runs here are deterministic stand-ins pending Milestone 2 (backend inference). | |
| import { parseSequences, type ParsedSequence } from '../lib/parseSequences' | |
| import type { RealModel } from './models' | |
| export const CONTACT_EMAIL = 'rsaqe@uwaterloo.ca' | |
| // --------------------------------------------------------------------------- | |
| // Sample sequences (for "load example" buttons) | |
| // --------------------------------------------------------------------------- | |
| export const SAMPLE_PEPTIDES = `VLPVPQK | |
| IPP | |
| NALKPDNR | |
| FEEPQQPQQR | |
| GQEIYIQQGK | |
| VPP | |
| SYTNGPQEIYIQQGK | |
| AGVALSR | |
| LLPHH | |
| CTLNR | |
| IYIVQGR | |
| FESSSQQFQG | |
| PLVHLLAQNIR | |
| AQQLQQLVLANLAAYSQEQQ | |
| EPQQPQQRG | |
| FAQPQQLFPE | |
| AADSFKHK` | |
| // Example protein for the allergen-origin screen: Tri a 39.0101, a recognized wheat | |
| // allergen (Triticum aestivum; WHO/IUIS, AllergenOnline). Matches the allergen index exactly. | |
| export const SAMPLE_PROTEIN = `>Tri_a_39.0101 wheat allergen (Triticum aestivum) | |
| MSPVVKKPEGRNTDTSDHHNQKTEWPELVGKSVEEAKKVILQDKSEAQIVVLPVGTIVTMEYRIDRVRLFVDSLDKIAQVPRVG` | |
| // --------------------------------------------------------------------------- | |
| // Deterministic pseudo-randomness (stable across renders) | |
| // --------------------------------------------------------------------------- | |
| function hashString(s: string): number { | |
| let h = 2166136261 | |
| for (let i = 0; i < s.length; i++) { | |
| h ^= s.charCodeAt(i) | |
| h = Math.imul(h, 16777619) | |
| } | |
| return (h >>> 0) / 4294967295 // -> [0, 1) | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Mock detectability prediction (illustrative, Milestone 2 will run the .joblib) | |
| // --------------------------------------------------------------------------- | |
| export interface DetectabilityRow { | |
| peptide: string | |
| length: number | |
| score: number | |
| prediction: 'Detectable' | 'Not detectable' | |
| model: string | |
| // Was this peptide in the selected model's training set? Flag so a memorised | |
| // high-confidence call isn't mistaken for generalisation. null = held out / unknown. | |
| inTraining?: 'positive' | 'negative' | null | |
| // Known bioactivity classes for this peptide (';'-joined), merged from the | |
| // bioactivity screen so the prediction output carries function too. '' = none found. | |
| bioactivities?: string | |
| } | |
| const DETECT_THRESHOLD = 0.5 | |
| export function runDetectability( | |
| input: string, | |
| model: RealModel, | |
| ): DetectabilityRow[] { | |
| const peptides = parseSequences(input) | |
| return peptides.map((p: ParsedSequence) => { | |
| const base = hashString(p.seq + '|' + model.code) | |
| const lengthBias = p.seq.length >= 7 && p.seq.length <= 20 ? 0.12 : -0.08 | |
| const hydrophobic = (p.seq.match(/[FLIVWY]/g)?.length ?? 0) / p.seq.length | |
| const score = Math.min( | |
| 0.99, | |
| Math.max(0.01, base * 0.7 + lengthBias + hydrophobic * 0.25), | |
| ) | |
| return { | |
| peptide: p.seq, | |
| length: p.seq.length, | |
| score: Number(score.toFixed(3)), | |
| prediction: score >= DETECT_THRESHOLD ? 'Detectable' : 'Not detectable', | |
| model: `${model.code} · ${model.species}`, | |
| } | |
| }) | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Bioactivity / allergen screening (exact-DB match, no offline compute possible) | |
| // --------------------------------------------------------------------------- | |
| export interface BioactivityRow { | |
| peptide: string | |
| bioactivities: string // ';'-joined canonical activity classes ('' = no hit) | |
| sources: string // ';'-joined DBs the hit came from | |
| igeEpitope: boolean // retains a documented linear IgE epitope (Q2) | |
| } | |
| export interface AllergenRow { | |
| protein: string | |
| isAllergen: boolean // recognized-allergen protein match (Q1) | |
| allergenName: string | |
| organism: string | |
| source: string | |
| } | |
| // Screening is an exact match against a static database, it cannot be faked offline. | |
| // The mocks parse the input and return empty hits; the page's offline badge makes clear | |
| // these are not real screening calls (no fabricated bioactivity/allergen claims). | |
| export function runBioactivityPeptides(input: string): BioactivityRow[] { | |
| return parseSequences(input).map((p: ParsedSequence) => ({ | |
| peptide: p.seq, | |
| bioactivities: '', | |
| sources: '', | |
| igeEpitope: false, | |
| })) | |
| } | |
| export function runAllergenOrigin(input: string): AllergenRow[] { | |
| return parseSequences(input).map((p: ParsedSequence) => ({ | |
| protein: p.seq.length > 60 ? p.seq.slice(0, 57) + '…' : p.seq, | |
| isAllergen: false, | |
| allergenName: '', | |
| organism: '', | |
| source: '', | |
| })) | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Mock training result (illustrative) | |
| // --------------------------------------------------------------------------- | |
| export interface FoldResult { | |
| fold: number | |
| rocAuc: number | |
| prAuc: number | |
| nTrain: number | |
| nTest: number | |
| } | |
| export interface TrainingResult { | |
| rocAuc: number | |
| prAuc: number | |
| folds: FoldResult[] | |
| nPositives: number | |
| nNegatives: number | |
| } | |
| export function runTraining(input: string, seed: string): TrainingResult { | |
| const peptides = parseSequences(input) | |
| const nPos = peptides.length | |
| const nNeg = Math.round(nPos * 1.0) | |
| const base = 0.72 + hashString(seed + nPos) * 0.18 // 0.72 - 0.90 | |
| const folds: FoldResult[] = Array.from({ length: 5 }, (_, i) => { | |
| const jitter = (hashString(seed + 'fold' + i) - 0.5) * 0.06 | |
| const roc = Math.min(0.97, Math.max(0.55, base + jitter)) | |
| return { | |
| fold: i + 1, | |
| rocAuc: Number(roc.toFixed(3)), | |
| prAuc: Number((roc - 0.06).toFixed(3)), | |
| nTrain: Math.round((nPos + nNeg) * 0.8), | |
| nTest: Math.round((nPos + nNeg) * 0.2), | |
| } | |
| }) | |
| const mean = folds.reduce((s, f) => s + f.rocAuc, 0) / folds.length | |
| return { | |
| rocAuc: Number(mean.toFixed(3)), | |
| prAuc: Number((mean - 0.06).toFixed(3)), | |
| folds, | |
| nPositives: nPos, | |
| nNegatives: nNeg, | |
| } | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Training-spec option sets, REAL vocabulary from the Step-4 pipeline | |
| // --------------------------------------------------------------------------- | |
| export const FEATURE_SET_OPTIONS = [ | |
| { id: 'phys', label: 'Physicochemical (14 descriptors)', desc: 'PeptideRanker descriptors' }, | |
| { id: 'pepbert', label: 'PepBERT', desc: 'PepBERT-large-UniParc embeddings' }, | |
| { id: 'esm2', label: 'ESM2-t12-35M', desc: 'ESM2 protein language model' }, | |
| { id: 'pep_phys', label: 'PepBERT ⊕ physicochemical', desc: 'Embedding + descriptors' }, | |
| { id: 'esm2_phys', label: 'ESM2 ⊕ physicochemical', desc: 'Top-ranked feature set (default)' }, | |
| { id: 'pfly', label: 'Pfly (sequence-only)', desc: 'dlomix DetectabilityModel, reads the sequence directly' }, | |
| ] | |
| export const CLASSIFIER_OPTIONS = [ | |
| { id: 'xgb', label: 'XGBoost', desc: 'Gradient-boosted trees (most consistent winner)' }, | |
| { id: 'rf', label: 'Random forest', desc: 'Bagged trees, balanced' }, | |
| { id: 'mlp', label: 'Neural net (MLP)', desc: 'MLP over features, standardized' }, | |
| { id: 'pfly', label: 'Pfly (fine-tuned)', desc: 'dlomix BiGRU, sequence-only (feature set ignored)' }, | |
| ] | |
| export const SPLIT_OPTIONS = [ | |
| { id: 'protein', label: 'Group by protein', desc: 'Production default, avoids peptide leakage' }, | |
| { id: 'cluster90', label: 'Cluster (90% id)', desc: 'MMseqs2 sequence-identity clusters' }, | |
| { id: 'cluster50', label: 'Cluster (50% id)', desc: 'Stricter homology grouping' }, | |
| { id: 'peptide', label: 'By peptide', desc: 'Leakage baseline (inflates AUROC)' }, | |
| ] | |
| export interface TrainingConfig { | |
| featureSet: string | |
| classifier: string | |
| split: string | |
| folds: number | |
| balance: string | |
| } | |
| export const DEFAULT_TRAINING_CONFIG: TrainingConfig = { | |
| featureSet: 'esm2_phys', | |
| classifier: 'xgb', | |
| split: 'protein', | |
| folds: 5, | |
| balance: '1:1', | |
| } | |