Architecture¶
System Design¶
graph TB
subgraph Input["User Input"]
SEEDS["Seed genes<br/>(or disease category)"]
end
subgraph Expand["Candidate Expansion"]
PATHWAY_EXP["Reactome pathway<br/>co-membership"]
end
subgraph Signals["Demand Signal Collection (async, parallel)"]
NIH["NIH RePORTER<br/>api.reporter.nih.gov"]
OA["OpenAlex<br/>api.openalex.org"]
CT["ClinicalTrials.gov<br/>clinicaltrials.gov/api/v2"]
GWAS["GWAS Catalog<br/>ebi.ac.uk/gwas/rest/api"]
end
subgraph Feasibility["Feasibility Scoring"]
BLOOD["HPA Blood Secretome<br/>(pass/fail filter)"]
XR["InterPro families<br/>(pairwise risk)"]
CONC["Concentration range<br/>(internal LOD data)"]
end
subgraph Evidence["Causal Evidence"]
PQTL["Open Targets Genetics<br/>pQTL colocalisation (H4)"]
GRAPHRAG["GraphRAG API<br/>Neptune KG queries"]
end
subgraph Selection["Optimization"]
NORM["Min-max normalize<br/>all dimensions to [0,1]"]
WEIGHT["Weighted composite<br/>Σ(wᵢ × sᵢ)"]
GREEDY["Greedy selector<br/>+ diversity constraints"]
PARETO["Pareto front<br/>(multi-objective)"]
end
subgraph Output["Output"]
PANEL["PanelCandidate<br/>{proteins, scores}"]
BACKTEST["Publication backtest"]
COMPARE["Competitor comparison"]
end
SEEDS --> PATHWAY_EXP
PATHWAY_EXP --> Signals
Signals --> NORM
Feasibility --> NORM
Evidence --> NORM
NORM --> WEIGHT
WEIGHT --> GREEDY
WEIGHT --> PARETO
GREEDY --> PANEL
PANEL --> BACKTEST
PANEL --> COMPARE
Data Pipeline¶
sequenceDiagram
participant U as User
participant P as Pipeline
participant R as Reactome
participant API as External APIs
participant C as Cache
U->>P: score(genes)
P->>C: Check cache
alt Cache hit
C-->>P: Cached signals
else Cache miss
P->>API: NIH + OpenAlex + CT.gov + GWAS (parallel)
API-->>P: Raw counts
P->>C: Store (TTL 24h)
end
P->>P: Normalize [0,1]
P->>P: Compute composite
P-->>U: Ranked DataFrame
Module Dependency Graph¶
graph LR
CLI["cli.py"] --> PIPE["pipeline.py"]
TUI["tui.py"] --> PIPE
PIPE --> SIG["signals/"]
PIPE --> FEAS["feasibility/"]
PIPE --> SEL["selection/"]
PIPE --> EXP["expand.py"]
PIPE --> EV["evidence.py"]
EV --> PQTL["evidence_pqtl.py"]
SIG --> CACHE["cache.py"]
PQTL --> CACHE
SEL --> DATA["data/"]
FEAS --> DATA
EXP --> DATA
DATA --> BIOINGEST[("bioingest/<br/>bulk data")]
Scoring Dimensions¶
| Dimension | Range | Sources | Default weight | Interpretation |
|---|---|---|---|---|
demand |
[0,1] | NIH grants, pub velocity, clinical trials, GWAS | 1.0 | Who wants this measured? |
evidence |
[0,1] | pQTL H4 > 0.8 count, KG disease associations | 1.0 | Is this causally important? |
feasibility |
[0,1] | Blood secretome, LOD, cross-reactivity | 1.0 | Can we technically build it? |
uniqueness |
[0,1] | 1/(1 + n_competitors_measuring) | 1.0 | Does it differentiate us? |
Normalization¶
All raw scores are min-max normalized within the candidate set before weighting:
This ensures: - Weights actually control trade-offs (not dominated by high-magnitude signals) - Scores are interpretable: 1.0 = best in set, 0.0 = worst in set - Equal-value candidates get 0.5 (midpoint)
Composite Aggregation¶
composite = w_demand × norm_demand
+ w_evidence × norm_evidence
+ w_feasibility × norm_feasibility
+ w_uniqueness × norm_uniqueness
Default weights are all 1.0. Override with --weights demand=3,uniqueness=2 to prioritize.
Caching Strategy¶
| Source | Cache key | TTL | Rationale |
|---|---|---|---|
| NIH RePORTER | nih_reporter:{gene} |
24h | Grants update infrequently |
| OpenAlex | pub_velocity:{gene} |
24h | New papers daily but trends stable |
| GWAS Catalog | gwas:{gene} |
7d | Updated quarterly |
| pQTL evidence | pqtl:{gene} |
7d | Genetic data rarely changes |
| Ensembl IDs | ensembl_id:{gene} |
30d | Static mapping |
Optimizer Algorithm¶
Greedy selection (default):
1. Sort candidates by composite_score descending
2. For each candidate in order:
- Skip if feasibility_score < 0.5 (not blood-detectable)
- Skip if in exclude_proteins set
- Skip if max_per_pathway constraint violated
- Add to panel
3. Stop at panel_size
Pareto optimization (--pareto flag):
1. Generate N candidate panels, each optimized for a single objective
2. Filter to Pareto front: panels where no other panel dominates on ALL objectives
3. Return all non-dominated panels for human decision-making
Expansion Algorithm¶
graph TD
S[10 seed genes] --> P[Find all Reactome pathways<br/>containing any seed]
P --> M[Collect all OTHER members<br/>of those pathways]
M --> R[Rank by shared pathway count<br/>(more overlap = more relevant)]
R --> T[Take top N to reach target size]
T --> C[50 expanded candidates]
A protein appearing in 5 pathways shared with seeds is more biologically relevant than one appearing in only 1 shared pathway.