pen-stack 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. pen_stack/__init__.py +2 -0
  2. pen_stack/_resources.py +34 -0
  3. pen_stack/active/__init__.py +20 -0
  4. pen_stack/active/acquire.py +165 -0
  5. pen_stack/active/brains.py +74 -0
  6. pen_stack/active/campaign.py +109 -0
  7. pen_stack/active/design.py +66 -0
  8. pen_stack/active/validate.py +104 -0
  9. pen_stack/adapt/__init__.py +14 -0
  10. pen_stack/adapt/finetune.py +33 -0
  11. pen_stack/adapt/ingest.py +86 -0
  12. pen_stack/adapt/pipeline.py +101 -0
  13. pen_stack/adapt/recalibrate.py +58 -0
  14. pen_stack/adapt/report.py +130 -0
  15. pen_stack/agent/__init__.py +1 -0
  16. pen_stack/agent/cite.py +175 -0
  17. pen_stack/agent/co_scientist.py +262 -0
  18. pen_stack/agent/epistemic.py +102 -0
  19. pen_stack/agent/guardrails.py +67 -0
  20. pen_stack/agent/mcp_server.py +215 -0
  21. pen_stack/agent/orchestrator.py +112 -0
  22. pen_stack/agent/orchestrator_live.py +56 -0
  23. pen_stack/agent/pen_agent.py +242 -0
  24. pen_stack/agent/scope.py +60 -0
  25. pen_stack/agent/tools.py +130 -0
  26. pen_stack/api/__init__.py +12 -0
  27. pen_stack/api/manifest.py +160 -0
  28. pen_stack/atlas/__init__.py +1 -0
  29. pen_stack/atlas/atlas.parquet +0 -0
  30. pen_stack/atlas/build_wtkb.py +80 -0
  31. pen_stack/atlas/crosslink.py +179 -0
  32. pen_stack/atlas/expand.py +190 -0
  33. pen_stack/atlas/guide_design.py +178 -0
  34. pen_stack/atlas/schema.py +59 -0
  35. pen_stack/atlas/scorecard.py +134 -0
  36. pen_stack/atlas/scorecard_v3.parquet +0 -0
  37. pen_stack/atlas/universe.py +75 -0
  38. pen_stack/atlas/universe_v3.parquet +0 -0
  39. pen_stack/atlas/variant_propose.py +155 -0
  40. pen_stack/atlas/writer_efficiency.py +184 -0
  41. pen_stack/atlas/writer_predict.py +229 -0
  42. pen_stack/atlas/writer_recommend.py +170 -0
  43. pen_stack/atlas/writer_verify.py +167 -0
  44. pen_stack/atlas/wtkb.parquet +0 -0
  45. pen_stack/bridge/__init__.py +1 -0
  46. pen_stack/bridge/activity.py +52 -0
  47. pen_stack/bridge/cli.py +65 -0
  48. pen_stack/bridge/fold_qc.py +53 -0
  49. pen_stack/bridge/guide_qc.py +87 -0
  50. pen_stack/bridge/ingest.py +139 -0
  51. pen_stack/bridge/offtarget.py +191 -0
  52. pen_stack/bridge/offtarget_energetics.py +105 -0
  53. pen_stack/bridge/ortholog_screen.py +73 -0
  54. pen_stack/bridge/pipeline.py +83 -0
  55. pen_stack/build/__init__.py +16 -0
  56. pen_stack/build/cloudlab.py +74 -0
  57. pen_stack/build/ingest.py +47 -0
  58. pen_stack/build/protocol.py +82 -0
  59. pen_stack/build/simlab.py +30 -0
  60. pen_stack/cli.py +126 -0
  61. pen_stack/data/__init__.py +1 -0
  62. pen_stack/data/encode.py +84 -0
  63. pen_stack/data/genome.py +71 -0
  64. pen_stack/data/ingest_chromatin.py +119 -0
  65. pen_stack/data/ingest_integration.py +112 -0
  66. pen_stack/data/ingest_safety_annot.py +201 -0
  67. pen_stack/data/ingest_trip.py +76 -0
  68. pen_stack/design/__init__.py +14 -0
  69. pen_stack/design/capsid_generate.py +62 -0
  70. pen_stack/design/generate.py +70 -0
  71. pen_stack/design/pareto.py +70 -0
  72. pen_stack/design/space.py +137 -0
  73. pen_stack/design/writer_variants.py +121 -0
  74. pen_stack/env/__init__.py +1 -0
  75. pen_stack/env/genome_writing_env.py +248 -0
  76. pen_stack/env/policies.py +94 -0
  77. pen_stack/graph/__init__.py +21 -0
  78. pen_stack/graph/build.py +133 -0
  79. pen_stack/graph/cell_types.py +58 -0
  80. pen_stack/graph/ingest.py +132 -0
  81. pen_stack/graph/query.py +148 -0
  82. pen_stack/graph/schema.py +100 -0
  83. pen_stack/loop/__init__.py +15 -0
  84. pen_stack/loop/continual.py +61 -0
  85. pen_stack/loop/cycle.py +84 -0
  86. pen_stack/loop/drift.py +41 -0
  87. pen_stack/mech/__init__.py +1 -0
  88. pen_stack/mech/classify_atlas.py +71 -0
  89. pen_stack/mech/pfam_whitelist.yaml +247 -0
  90. pen_stack/mech/whitelist.py +66 -0
  91. pen_stack/monitor/__init__.py +1 -0
  92. pen_stack/monitor/europepmc.py +32 -0
  93. pen_stack/monitor/run.py +57 -0
  94. pen_stack/monitor/triage.py +63 -0
  95. pen_stack/oracles/__init__.py +65 -0
  96. pen_stack/oracles/affinity.py +116 -0
  97. pen_stack/oracles/cache.py +53 -0
  98. pen_stack/oracles/energetics.py +33 -0
  99. pen_stack/oracles/genome.py +167 -0
  100. pen_stack/oracles/protein_design.py +136 -0
  101. pen_stack/oracles/reliability.py +64 -0
  102. pen_stack/oracles/rna.py +28 -0
  103. pen_stack/oracles/schema.py +77 -0
  104. pen_stack/oracles/status.py +123 -0
  105. pen_stack/oracles/structure.py +42 -0
  106. pen_stack/oracles/structure_run.py +76 -0
  107. pen_stack/oracles/vcell.py +74 -0
  108. pen_stack/planner/__init__.py +1 -0
  109. pen_stack/planner/ada_risk.py +64 -0
  110. pen_stack/planner/antipeg_oracle.py +75 -0
  111. pen_stack/planner/capsid_epitope_oracle.py +135 -0
  112. pen_stack/planner/cargo.py +56 -0
  113. pen_stack/planner/cargo_polish.py +146 -0
  114. pen_stack/planner/chromosome.py +106 -0
  115. pen_stack/planner/delivery.py +55 -0
  116. pen_stack/planner/delivery_constraints.py +110 -0
  117. pen_stack/planner/delivery_immune.py +61 -0
  118. pen_stack/planner/delivery_immunology.py +222 -0
  119. pen_stack/planner/delivery_predict.py +196 -0
  120. pen_stack/planner/delivery_vehicles.py +37 -0
  121. pen_stack/planner/genotoxicity_oracle.py +112 -0
  122. pen_stack/planner/immune_mhc2.py +154 -0
  123. pen_stack/planner/immune_profile.py +292 -0
  124. pen_stack/planner/innate_sensing.py +135 -0
  125. pen_stack/planner/multiplex.py +110 -0
  126. pen_stack/planner/optimize.py +278 -0
  127. pen_stack/planner/pipeline.py +87 -0
  128. pen_stack/planner/report.py +26 -0
  129. pen_stack/planner/router.py +57 -0
  130. pen_stack/planner/seroprevalence_oracle.py +92 -0
  131. pen_stack/planner/target_site.py +118 -0
  132. pen_stack/rag/__init__.py +1 -0
  133. pen_stack/rag/corpus.py +133 -0
  134. pen_stack/rag/embed.py +98 -0
  135. pen_stack/rag/ground.py +131 -0
  136. pen_stack/rag/index.py +53 -0
  137. pen_stack/rag/llm.py +215 -0
  138. pen_stack/rag/qa.py +105 -0
  139. pen_stack/rag/retrieve.py +48 -0
  140. pen_stack/rules/__init__.py +9 -0
  141. pen_stack/rules/evaluators.py +318 -0
  142. pen_stack/rules/loader.py +31 -0
  143. pen_stack/rules/schema.py +99 -0
  144. pen_stack/rules/solver.py +43 -0
  145. pen_stack/rules/spec.py +78 -0
  146. pen_stack/safety/__init__.py +21 -0
  147. pen_stack/safety/audit.py +90 -0
  148. pen_stack/safety/gate.py +58 -0
  149. pen_stack/safety/pfam_scan.py +157 -0
  150. pen_stack/safety/policy.py +69 -0
  151. pen_stack/safety/redteam.py +71 -0
  152. pen_stack/safety/registry.py +255 -0
  153. pen_stack/safety/screen.py +59 -0
  154. pen_stack/safety/standards.py +141 -0
  155. pen_stack/score/__init__.py +1 -0
  156. pen_stack/score/recalibrate.py +77 -0
  157. pen_stack/score/therapeutic.py +85 -0
  158. pen_stack/server/__init__.py +1 -0
  159. pen_stack/server/api.py +647 -0
  160. pen_stack/spec/__init__.py +18 -0
  161. pen_stack/spec/clarify.py +42 -0
  162. pen_stack/spec/extract.py +406 -0
  163. pen_stack/spec/resolvers/__init__.py +17 -0
  164. pen_stack/spec/resolvers/cell.py +51 -0
  165. pen_stack/spec/resolvers/chem.py +32 -0
  166. pen_stack/spec/resolvers/feature.py +36 -0
  167. pen_stack/spec/resolvers/gene.py +43 -0
  168. pen_stack/spec/resolvers/locus.py +29 -0
  169. pen_stack/spec/resolvers/phenotype.py +37 -0
  170. pen_stack/spec/satisfy.py +114 -0
  171. pen_stack/spec/service.py +33 -0
  172. pen_stack/spec/writespec.py +252 -0
  173. pen_stack/twin/__init__.py +14 -0
  174. pen_stack/twin/calibrate.py +61 -0
  175. pen_stack/twin/data/__init__.py +12 -0
  176. pen_stack/twin/data/position_effect.py +245 -0
  177. pen_stack/twin/mechanistic.py +147 -0
  178. pen_stack/twin/outcome.py +132 -0
  179. pen_stack/twin/position_effect.py +454 -0
  180. pen_stack/ui/__init__.py +1 -0
  181. pen_stack/ui/app.py +713 -0
  182. pen_stack/validate/__init__.py +1 -0
  183. pen_stack/validate/adapt_demo.py +69 -0
  184. pen_stack/validate/agent_eval.py +117 -0
  185. pen_stack/validate/bench_adversarial_tasks.py +118 -0
  186. pen_stack/validate/bench_coscientist_tasks.py +60 -0
  187. pen_stack/validate/bench_graph_tasks.py +64 -0
  188. pen_stack/validate/bench_rule_tasks.py +84 -0
  189. pen_stack/validate/bench_trust_tasks.py +92 -0
  190. pen_stack/validate/bench_writetype_tasks.py +101 -0
  191. pen_stack/validate/blind_gsh_discovery.py +261 -0
  192. pen_stack/validate/cargo_directionality.py +57 -0
  193. pen_stack/validate/closed_loop.py +63 -0
  194. pen_stack/validate/durability_baselines.py +185 -0
  195. pen_stack/validate/experiment_design.py +65 -0
  196. pen_stack/validate/expr_controls.py +39 -0
  197. pen_stack/validate/forward_hypotheses.py +104 -0
  198. pen_stack/validate/generative_design.py +62 -0
  199. pen_stack/validate/guide_qc_demo.py +69 -0
  200. pen_stack/validate/heldout_celltype_expr.py +32 -0
  201. pen_stack/validate/immune_calibration.py +133 -0
  202. pen_stack/validate/intent_specification.py +82 -0
  203. pen_stack/validate/known_biology_expr.py +38 -0
  204. pen_stack/validate/offtarget_energetics_eval.py +144 -0
  205. pen_stack/validate/out_of_scope_refusal.py +82 -0
  206. pen_stack/validate/outcome_calibration.py +194 -0
  207. pen_stack/validate/outcome_prediction.py +76 -0
  208. pen_stack/validate/paper3_benchmark.py +165 -0
  209. pen_stack/validate/paper4_real_validation.py +144 -0
  210. pen_stack/validate/paper4_validation.py +82 -0
  211. pen_stack/validate/protocol_safety.py +62 -0
  212. pen_stack/validate/safety_screening.py +72 -0
  213. pen_stack/validate/selective_prediction.py +104 -0
  214. pen_stack/validate/seq_vs_measured.py +134 -0
  215. pen_stack/validate/target_site_controls.py +65 -0
  216. pen_stack/validate/uncertainty_eval.py +244 -0
  217. pen_stack/validate/ungrounded_baseline.py +234 -0
  218. pen_stack/validate/within_locus_ranking.py +84 -0
  219. pen_stack/validate/writer_recovery.py +91 -0
  220. pen_stack/verify/__init__.py +5 -0
  221. pen_stack/verify/proof.py +206 -0
  222. pen_stack/verify/schema.py +53 -0
  223. pen_stack/verify/service.py +191 -0
  224. pen_stack/web/__init__.py +18 -0
  225. pen_stack/web/guide.py +110 -0
  226. pen_stack/web/llm.py +393 -0
  227. pen_stack/web/llm_provider.py +119 -0
  228. pen_stack/web/router.py +119 -0
  229. pen_stack/web/server.py +96 -0
  230. pen_stack/web/tools.py +197 -0
  231. pen_stack/wgenome/__init__.py +1 -0
  232. pen_stack/wgenome/chromatin_seq.py +83 -0
  233. pen_stack/wgenome/durability.py +108 -0
  234. pen_stack/wgenome/export_tracks.py +52 -0
  235. pen_stack/wgenome/features.py +82 -0
  236. pen_stack/wgenome/genotoxic_blocklist.py +88 -0
  237. pen_stack/wgenome/gsh_baseline.py +154 -0
  238. pen_stack/wgenome/mesh_features.py +61 -0
  239. pen_stack/wgenome/offtarget_assay.py +80 -0
  240. pen_stack/wgenome/offtarget_bridge.py +47 -0
  241. pen_stack/wgenome/offtarget_cast.py +97 -0
  242. pen_stack/wgenome/offtarget_data.py +148 -0
  243. pen_stack/wgenome/offtarget_enumerate.py +274 -0
  244. pen_stack/wgenome/offtarget_integrase.py +155 -0
  245. pen_stack/wgenome/offtarget_nuclease.py +123 -0
  246. pen_stack/wgenome/offtarget_paste.py +41 -0
  247. pen_stack/wgenome/offtarget_predict.py +282 -0
  248. pen_stack/wgenome/ood.py +135 -0
  249. pen_stack/wgenome/providers.py +278 -0
  250. pen_stack/wgenome/safety.py +69 -0
  251. pen_stack/wgenome/structure3d.py +212 -0
  252. pen_stack/wgenome/uncertainty.py +250 -0
  253. pen_stack/wgenome/writability.py +72 -0
  254. pen_stack-0.1.0.dist-info/METADATA +401 -0
  255. pen_stack-0.1.0.dist-info/RECORD +259 -0
  256. pen_stack-0.1.0.dist-info/WHEEL +5 -0
  257. pen_stack-0.1.0.dist-info/entry_points.txt +3 -0
  258. pen_stack-0.1.0.dist-info/licenses/LICENSE +21 -0
  259. pen_stack-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,184 @@
1
+ """Writer-Efficiency dataset, the curated, literature-sourced efficiency table.
2
+
3
+ The writer frontier crossed into quantitative human-cell data in 2023-26, but the efficiency numbers are scattered
4
+ across a dozen papers with no unified resource. This module curates them into ONE schema so a learned cross-family
5
+ efficiency predictor (`atlas.writer_predict`) can be trained and a held-out benchmark (`benchmarks/writer_efficiency/`,
6
+ the Writer-Efficiency Bench) can be sealed. The curated dataset is itself the standalone contribution.
7
+
8
+ NO FABRICATION. Every record carries its DOI, the verbatim source quote, and a `source_access` provenance grade:
9
+ * `pmc_verbatim`, quoted from the open-access PMC full text (highest confidence)
10
+ * `abstract`, quoted from the published abstract
11
+ * `secondary`, a number reported by a reliable secondary source (press release / summary) where the
12
+ primary full text is paywalled; flagged so it can be excluded from the strict benchmark.
13
+ Range values (e.g. "~10-20%", "4-22%") are stored as their midpoint in `efficiency_pct` with the raw string in
14
+ `efficiency_raw`; the exact quote travels in `quote`. Efficiencies are % integration in the stated cell type
15
+ (human unless noted); bacterial (E. coli) rows are labelled and excluded from the human-cell benchmark.
16
+ """
17
+ from __future__ import annotations
18
+
19
+ from pathlib import Path
20
+
21
+ import pandas as pd
22
+
23
+ from pen_stack._resources import project_root
24
+
25
+ COLUMNS = ["system", "family", "write_type", "variant", "cargo_bp", "locus", "cell_type", "organism",
26
+ "delivery", "efficiency_pct", "efficiency_raw", "specificity_pct", "source_access", "doi", "quote"]
27
+
28
+ # Family keys align with the Writer Atlas (wtkb). PE-installed-att systems (PASTE/PASSIGE) -> PE_integrase;
29
+ # pre-installed-att recombinase-only systems -> serine_integrase; bridge -> bridge_IS110; CAST -> CAST_VK.
30
+ _R = [] # records
31
+
32
+
33
+ def _rec(system, family, write_type, variant, cargo_bp, locus, cell_type, organism, delivery,
34
+ eff, eff_raw, doi, quote, source_access="pmc_verbatim", spec=None):
35
+ _R.append(dict(system=system, family=family, write_type=write_type, variant=variant, cargo_bp=cargo_bp,
36
+ locus=locus, cell_type=cell_type, organism=organism, delivery=delivery,
37
+ efficiency_pct=float(eff), efficiency_raw=eff_raw, specificity_pct=spec,
38
+ source_access=source_access, doi=doi, quote=quote))
39
+
40
+
41
+ # ---- PASTE, Yarnall et al., Nat Biotechnol 2023 (41:500-512), 10.1038/s41587-022-01527-4 (PMC10257351) ----
42
+ _P = "10.1038/s41587-022-01527-4"
43
+ _rec("PASTEv1", "PE_integrase", "insertion", "WT", 969, "ACTB", "HEK293FT", "human", "plasmid", 15, "~15%", _P,
44
+ "BxbINT integrase showed the highest integration rate (~15%) at the targeted ACTB locus")
45
+ _rec("PASTEv1", "PE_integrase", "insertion", "WT", 969, "ACTB", "K562", "human", "plasmid", 15, "~15%", _P,
46
+ "PASTEv1 had ~15% gene integration activity in K562 cells")
47
+ _rec("PASTEv1", "PE_integrase", "insertion", "WT", 969, "ACTB", "primary_T", "human", "plasmid", 5, "~5%", _P,
48
+ "around 5% efficiency in primary human T cells")
49
+ _rec("PASTEv1", "PE_integrase", "insertion", "WT", 969, "ACTB", "primary_hepatocyte", "human", "plasmid", 5, "~5%",
50
+ _P, "PASTEv1 was capable of ~5% gene integration at the ACTB locus in non-dividing quiescent human primary hepatocytes")
51
+ _rec("PASTEv3", "PE_integrase", "insertion", "WT", 36000, "ACTB", "HEK293FT", "human", "plasmid", 15, "~10-20%", _P,
52
+ "PASTEv3...achieved precise integration of templates as large as ~36,000 bp with ~10-20% integration efficiency at ACTB and LMNB1")
53
+ _rec("PASTE", "PE_integrase", "insertion", "WT", 3000, "ACTB", "HEK293FT", "human", "plasmid", 13, "4-22%", _P,
54
+ "integration frequencies between 4% and 22% depending on the gene and insertion locus (cargos 969-4,906 bp)")
55
+ _rec("PASTE_AdV", "PE_integrase", "insertion", "WT", 1000, "ACTB", "HepG2", "human", "adenovirus", 55, "~50-60%", _P,
56
+ "integration of up to ~50-60% with viral-only delivery in HEK293FT and HepG2 cells")
57
+ _rec("PASTE_AdV", "PE_integrase", "insertion", "WT", 1000, "ACTB", "primary_hepatocyte", "human", "adenovirus",
58
+ 10.5, "up to 10.5%", _P, "editing efficiencies up to 10.5% in a cargo-dependent fashion (primary human hepatocytes)")
59
+ _rec("PASTE_invivo", "PE_integrase", "insertion", "WT", 1000, "ACTB", "hepatocyte_in_vivo", "human", "AAV", 2.5,
60
+ "up to 2.5%", _P, "PASTE was capable of integration rates as high as 2.5% in the human hepatocytes in the chimeric liver")
61
+
62
+ # ---- (ee)PASSIGE, Pandey/Gao/Krasnow/Liu et al., Nat Biomed Eng 2025 (9:22-39), 10.1038/s41551-024-01227-1 (PMC11754103) ----
63
+ _E = "10.1038/s41551-024-01227-1"
64
+ _rec("eeBxb1", "serine_integrase", "insertion", "V105I", 5600, "AAVS1", "HEK293T", "human", "plasmid", 60, "60%", _E,
65
+ "The V105I mutant supported 60% and 39% integration efficiencies at AAVS1 and CCR5, respectively (pre-installed attB)")
66
+ _rec("eeBxb1", "serine_integrase", "insertion", "V105I", 5600, "CCR5", "HEK293T", "human", "plasmid", 39, "39%", _E,
67
+ "The V105I mutant supported 60% and 39% integration efficiencies at AAVS1 and CCR5, respectively (pre-installed attB)")
68
+ _rec("PASSIGE", "PE_integrase", "insertion", "WT_Bxb1", 5600, "AAVS1", "HEK293T", "human", "plasmid", 13, "13%", _E,
69
+ "evoPASSIGE: 27%, 22% vs. PASSIGE: 13%, 10% (at AAVS1 and CCR5)")
70
+ _rec("PASSIGE", "PE_integrase", "insertion", "WT_Bxb1", 5600, "CCR5", "HEK293T", "human", "plasmid", 10, "10%", _E,
71
+ "evoPASSIGE: 27%, 22% vs. PASSIGE: 13%, 10% (at AAVS1 and CCR5)")
72
+ _rec("evoPASSIGE", "PE_integrase", "insertion", "evoBxb1", 5600, "AAVS1", "HEK293T", "human", "plasmid", 27, "27%", _E,
73
+ "evoPASSIGE: 27%, 22% (at AAVS1 and CCR5)")
74
+ _rec("eePASSIGE", "PE_integrase", "insertion", "eeBxb1", 5600, "AAVS1", "HEK293T", "human", "plasmid", 36, "36%", _E,
75
+ "36% and 27% at the AAVS1 and CCR5 loci, respectively")
76
+ _rec("eePASSIGE", "PE_integrase", "insertion", "eeBxb1", 5600, "CCR5", "HEK293T", "human", "plasmid", 27, "27%", _E,
77
+ "36% and 27% at the AAVS1 and CCR5 loci, respectively")
78
+ _rec("eePASSIGE", "PE_integrase", "insertion", "eeBxb1", 5600, "ROSA26", "N2a", "mouse", "plasmid", 20, "20%", _E,
79
+ "20% efficiency compared with 9.5% with evoPASSIGE and 3.2% with PASSIGE (Rosa26, N2a)")
80
+ _rec("eePASSIGE", "PE_integrase", "insertion", "eeBxb1", 5600, "GBA1", "HEK293T", "human", "plasmid", 35, "35%", _E,
81
+ "therapeutic-gene integration; GBA1 among loci with 32% average and a minimum of 23% integration")
82
+ _rec("eePASSIGE", "PE_integrase", "insertion", "eeBxb1", 5600, "FANCA", "HEK293T", "human", "plasmid", 46, "46%", _E,
83
+ "therapeutic-gene integration; FANCA among loci (32% average, minimum 23%)")
84
+ _rec("eePASSIGE", "PE_integrase", "insertion", "eeBxb1", 5600, "B2M", "HEK293T", "human", "plasmid", 32, "32%", _E,
85
+ "32% average integration and a minimum of 23% integration (B2M)")
86
+ _rec("eePASSIGE", "PE_integrase", "insertion", "eeBxb1", 5600, "ALB", "HEK293T", "human", "plasmid", 31, "31%", _E,
87
+ "therapeutic-gene integration; ALB ~31% (32% average, minimum 23%)")
88
+ _rec("eePASSIGE", "PE_integrase", "insertion", "eeBxb1", 5800, "COL7A1", "primary_fibroblast", "human", "mRNA", 30,
89
+ "30%", _E, "eePASSIGE exhibited 30% and 18% average integration in the COL7A1 and FANCA loci, respectively (primary fibroblasts, mRNA)")
90
+ _rec("eePASSIGE", "PE_integrase", "insertion", "eeBxb1", 5800, "FANCA", "primary_fibroblast", "human", "mRNA", 18,
91
+ "18%", _E, "eePASSIGE exhibited 30% and 18% average integration in the COL7A1 and FANCA loci, respectively (primary fibroblasts)")
92
+ _rec("eePASSIGE", "PE_integrase", "insertion", "eeBxb1", 10500, "AAVS1", "HEK293T", "human", "plasmid", 35,
93
+ "up to 35%", _E, "eePASSIGE achieved up to 35% targeted integration of the 10.5-kb donor plasmid")
94
+ _rec("eeBxb1", "serine_integrase", "insertion", "eeBxb1", 5600, "CCR5", "iPSC", "human", "plasmid", 3.8, "3.8%", _E,
95
+ "7.3-fold improvement in integration efficiency with eeBxb1 (3.8%) (human iPS cells)")
96
+ _rec("PASTE", "PE_integrase", "insertion", "WT_Bxb1", 5600, "8loci_avg", "HEK293T", "human", "plasmid", 3.8,
97
+ "3.8% avg", _E, "eePASSIGE, evoPASSIGE, PASSIGE and PASTE mediated...average efficiency of 22%, 17%, 7.8% and 3.8%")
98
+
99
+ # ---- Hyperactive integrases, Hew et al., Nucleic Acids Res 2024 (52(14):e64), 10.1093/nar/gkae534 (PMC) ----
100
+ _H = "10.1093/nar/gkae534"
101
+ _rec("Bxb1", "serine_integrase", "insertion", "WT", 6600, "Xq22.1", "K562", "human", "plasmid", 2.7, "2.7%", _H,
102
+ "11.2-fold (2.7% versus 30.3%) (WT versus evolved combination, PE-installed att)")
103
+ _rec("Bxb1", "serine_integrase", "insertion", "c22_I87L_H95Y_V122M_A369P_E434G", 6600, "Xq22.1", "K562", "human",
104
+ "plasmid", 30.3, "30.3%", _H, "11.2-fold (2.7% versus 30.3%) (evolved combination mutant)")
105
+ _rec("Bxb1", "serine_integrase", "insertion", "WT", 15700, "attP_preinstalled", "HEK293T", "human", "plasmid",
106
+ 22.8, "22.8%", _H, "56.2% integration by the evolved Bxb1 integrase compared to 22.8% for wildtype (15.7 kb vWF cargo)")
107
+ _rec("Bxb1", "serine_integrase", "insertion", "evolved_combination", 15700, "attP_preinstalled", "HEK293T", "human",
108
+ "plasmid", 56.2, "56.2%", _H, "56.2% integration by the evolved Bxb1 integrase (15.7 kb vWF cargo)")
109
+ _rec("PhiC31", "serine_integrase", "insertion", "P2", 6600, "ROSA26", "HEK293T", "human", "plasmid", 1.3, "1.3%", _H,
110
+ "greatest improvement was between P2 (1.3%) and a 36-hour evolved variant P2-L2-1 (12.1%)")
111
+ _rec("PhiC31", "serine_integrase", "insertion", "P2-L2-1", 6600, "ROSA26", "HEK293T", "human", "plasmid", 12.1,
112
+ "12.1%", _H, "P2 (1.3%) and a 36-hour evolved variant P2-L2-1 (12.1%)")
113
+ _rec("PhiC31", "serine_integrase", "insertion", "P3-L1-2", 6600, "ROSA26", "HEK293T", "human", "plasmid", 18.4,
114
+ "18.4%", _H, "P3-L1-2 (18.4% at ROSA26)")
115
+ _rec("PhiC31", "serine_integrase", "insertion", "P3-L1-13", 6600, "AAVS1", "HEK293T", "human", "plasmid", 11.1,
116
+ "11.1%", _H, "integration efficiencies for P3 reaching 11.1% and 3.0% at ROSA26 and AAVS1")
117
+
118
+ # ---- CAST, ShCAST (Strecker 2019, 10.1126/science.aax9181, E. coli) + evoCAST (Science 2025, 10.1126/science.adt5199, PMC12326709) ----
119
+ _rec("ShCAST", "CAST_VK", "insertion", "WT", 5000, "ecoli_genome", "E_coli", "bacterial", "plasmid", 80, "up to 80%",
120
+ "10.1126/science.aax9181",
121
+ "ShCAST integrates DNA into unique sites in the E. coli genome with frequencies of up to 80% without positive selection")
122
+ _rec("PseCAST", "CAST_VK", "insertion", "WT", 1000, "ALB", "HEK293T", "human", "plasmid", 0.023, "0.023%",
123
+ "10.1126/science.adt5199", "5.7% targeted integration efficiency...compared to 0.023% for wild-type PseCAST (ALB)")
124
+ _rec("evoCAST", "CAST_VK", "insertion", "evolved", 1000, "ALB", "HEK293T", "human", "plasmid", 5.7, "5.7%",
125
+ "10.1126/science.adt5199", "5.7% targeted integration efficiency...compared to 0.023% for wild-type PseCAST (ALB)")
126
+ _rec("evoCAST", "CAST_VK", "insertion", "evolved", 1000, "TRAC", "HEK293T", "human", "plasmid", 13, "13%",
127
+ "10.1126/science.adt5199", "13% integration...compared to 0.061% by wild-type PseCAST (TRAC)")
128
+ _rec("evoCAST", "CAST_VK", "insertion", "evolved", 5000, "14loci_avg", "HEK293T", "human", "plasmid", 19, "~10-25%",
129
+ "10.1126/science.adt5199", "evoCAST averaged ~10-25% integration efficiencies of kilobase-size DNA cargoes across 14 tested human genomic sites; averaged 19%")
130
+
131
+ # ---- Bridge recombinases, enIS621 (Nat Commun 2026, 10.1038/s41467-026-74164-z), ISCro4 (Science) ----
132
+ _B = "10.1038/s41467-026-74164-z"
133
+ _rec("enIS621-tebRNA", "bridge_IS110", "insertion", "engineered", 1000, "endogenous_avg", "HEK293T", "human",
134
+ "plasmid", 6.82, "6.82%", _B, "6.82% for enIS621-tebRNA (average, two endogenous loci, HEK293T)",
135
+ source_access="secondary")
136
+ _rec("enIS621-tebRNA", "bridge_IS110", "insertion", "engineered", 1000, "endogenous_avg", "Jurkat", "human",
137
+ "plasmid", 14.76, "14.76%", _B, "14.76% for enIS621-tebRNA (Jurkat T cells)", source_access="secondary")
138
+ _rec("enIS621-tebRNA", "bridge_IS110", "insertion", "engineered", 1000, "best_locus", "multi_human", "human",
139
+ "plasmid", 27.75, "up to 27.75%", _B, "integration rates up to 27.75% for kilobase-scale DNA cargos",
140
+ source_access="abstract")
141
+ _rec("ISCro4", "bridge_IS110", "insertion", "WT", 1000, "human_genome", "human_cells", "human", "plasmid", 6,
142
+ ">6%", "10.1126/science.adz1884",
143
+ "facilitates donor DNA insertion at genomic sites with efficiencies surpassing 6% (Pelea et al. human cells)",
144
+ source_access="secondary")
145
+ _rec("ISCro4", "bridge_IS110", "insertion", "engineered_DMS", 1000, "human_genome", "human_cells", "human",
146
+ "plasmid", 20, "up to 20%", "10.1126/science.adz0276",
147
+ "up to 20% insertion efficiency into the human genome and genome-wide specificity as high as 82% (Perry et al.)",
148
+ source_access="secondary", spec=82)
149
+ _rec("IS621", "bridge_IS110", "insertion", "WT", 1000, "ecoli_genome", "E_coli", "bacterial", "plasmid", 60,
150
+ ">60%", "10.1038/s41586-024-07552-4",
151
+ "over 60% insertion efficiency of a desired gene in E. coli with over 94% specificity (Durrant et al.)",
152
+ source_access="secondary", spec=94)
153
+
154
+
155
+ def records() -> pd.DataFrame:
156
+ """The curated writer-efficiency table (one row per measured condition), with full per-row provenance."""
157
+ return pd.DataFrame(_R, columns=COLUMNS)
158
+
159
+
160
+ def human_cell(df: pd.DataFrame | None = None, strict: bool = False) -> pd.DataFrame:
161
+ """Human-cell rows only (excludes E. coli). `strict=True` keeps only pmc_verbatim + abstract sources
162
+ (drops secondary-source rows), the high-confidence subset for the strict benchmark."""
163
+ df = records() if df is None else df
164
+ out = df[df["organism"] == "human"].copy()
165
+ if strict:
166
+ out = out[out["source_access"].isin(["pmc_verbatim", "abstract"])]
167
+ return out.reset_index(drop=True)
168
+
169
+
170
+ def build_parquet(out: str | Path | None = None) -> Path:
171
+ """Write the SHA-lockable dataset parquet (data/writer_efficiency.parquet)."""
172
+ out = Path(out) if out else project_root() / "data/writer_efficiency.parquet"
173
+ out.parent.mkdir(parents=True, exist_ok=True)
174
+ records().to_parquet(out, index=False)
175
+ return out
176
+
177
+
178
+ def provenance_summary() -> dict:
179
+ df = records()
180
+ return {"n_records": int(len(df)), "n_human": int((df["organism"] == "human").sum()),
181
+ "by_family": df["family"].value_counts().to_dict(),
182
+ "by_source_access": df["source_access"].value_counts().to_dict(),
183
+ "n_dois": int(df["doi"].nunique()), "loci": sorted(df["locus"].unique()),
184
+ "cell_types": sorted(df["cell_type"].unique())}
@@ -0,0 +1,229 @@
1
+ """Learned cross-family writer-efficiency predictor.
2
+
3
+ Given `(family, variant-is-evolved, write-type, cargo size, locus context, cell-type class)`, predict the
4
+ integration **efficiency (%)** with a conformal interval, candidate-flagged. The baseline-to-beat is the
5
+ curated-KB prior (a writer's family-mean efficiency; the KB ranks by family). Pre-registered acceptance gate: the
6
+ learned model ships ONLY if it beats the family-mean baseline on **held-out family AND held-out
7
+ locus** (paired-bootstrap CI excludes 0); otherwise the KB ranking is retained and the negative is reported.
8
+ Never tune to the test.
9
+
10
+ This is a SMALL curated dataset (~38 human-cell records across 4 families), the benchmark + the dataset are the
11
+ standalone contribution, and a negative result is an expected outcome. Features are deliberately
12
+ interpretable (no ESM/Evo2 embeddings here, at N=38 they would overfit; variant *scoring* uses ESM3/Evo2 in
13
+ `design.writer_variants`). Every efficiency is a real published number (see `writer_efficiency`); nothing invented.
14
+ """
15
+ from __future__ import annotations
16
+
17
+ import pickle
18
+ from dataclasses import dataclass, field
19
+ from pathlib import Path
20
+
21
+ import numpy as np
22
+ import pandas as pd
23
+ from scipy.stats import spearmanr
24
+
25
+ from pen_stack.atlas.writer_efficiency import human_cell
26
+ from pen_stack.wgenome.uncertainty import ConformalRegressor
27
+
28
+ # interpretable feature construction -------------------------------------------------------------
29
+ _PRIMARY = {"primary_T", "primary_hepatocyte", "primary_fibroblast", "hepatocyte_in_vivo", "iPSC"}
30
+ _AGG_LOCI = {"8loci_avg", "14loci_avg", "endogenous_avg", "best_locus", "human_genome", "multi_human"}
31
+
32
+
33
+ def _evolved(variant: str) -> int:
34
+ v = str(variant).lower()
35
+ wt = v in ("wt", "wt_bxb1") or v.startswith("wt")
36
+ return 0 if wt else 1
37
+
38
+
39
+ def _celltype_class(ct: str) -> str:
40
+ if ct in _PRIMARY:
41
+ return "primary_or_ipsc"
42
+ return "cell_line"
43
+
44
+
45
+ def featurize(df: pd.DataFrame) -> pd.DataFrame:
46
+ f = pd.DataFrame(index=df.index)
47
+ f["family"] = df["family"].astype("category")
48
+ f["evolved"] = df["variant"].map(_evolved).astype(int)
49
+ f["log_cargo"] = np.log10(df["cargo_bp"].astype(float).clip(lower=1))
50
+ f["cell_class"] = df["cell_type"].map(_celltype_class).astype("category")
51
+ f["delivery"] = df["delivery"].astype("category")
52
+ return f
53
+
54
+
55
+ def specific_locus_mask(df: pd.DataFrame) -> np.ndarray:
56
+ """True for rows at a SPECIFIC named locus (excludes aggregate/avg/genome-wide pseudo-loci)."""
57
+ return ~df["locus"].isin(_AGG_LOCI)
58
+
59
+
60
+ # baselines --------------------------------------------------------------------------------------
61
+ def _family_mean_baseline(train: pd.DataFrame, test: pd.DataFrame) -> np.ndarray:
62
+ """The KB prior: predict a row's efficiency as the train family-mean (falls back to the global train mean
63
+ for an unseen family, which is exactly what happens under held-out-family)."""
64
+ gm = float(train["efficiency_pct"].mean())
65
+ fam_mean = train.groupby("family")["efficiency_pct"].mean().to_dict()
66
+ return np.array([fam_mean.get(f, gm) for f in test["family"]], float)
67
+
68
+
69
+ # model ------------------------------------------------------------------------------------------
70
+ def _fit_gbr(Xtr, ytr, seed: int = 42):
71
+ """Small, regularized GBR, appropriate for ~38 points (shallow, few estimators). One-hot the categoricals."""
72
+ from sklearn.ensemble import HistGradientBoostingRegressor
73
+ m = HistGradientBoostingRegressor(max_depth=2, max_iter=120, learning_rate=0.05,
74
+ l2_regularization=1.0, min_samples_leaf=4, random_state=seed)
75
+ m.fit(Xtr, ytr)
76
+ return m
77
+
78
+
79
+ def _encode(f_train: pd.DataFrame, f_test: pd.DataFrame):
80
+ """One-hot encode categoricals consistently across train/test."""
81
+ cat = ["family", "cell_class", "delivery"]
82
+ num = ["evolved", "log_cargo"]
83
+ Xtr = pd.get_dummies(f_train[cat + num], columns=cat).astype(float)
84
+ Xte = pd.get_dummies(f_test[cat + num], columns=cat).reindex(columns=Xtr.columns, fill_value=0.0).astype(float)
85
+ return Xtr, Xte
86
+
87
+
88
+ @dataclass
89
+ class WriterEfficiencyModel:
90
+ columns: list[str] = field(default_factory=list)
91
+ model: object | None = None
92
+ conformal: ConformalRegressor | None = None
93
+ meta: dict = field(default_factory=dict)
94
+
95
+ def fit(self, df: pd.DataFrame | None = None, seed: int = 42) -> "WriterEfficiencyModel":
96
+ df = human_cell(strict=False) if df is None else df
97
+ f = featurize(df)
98
+ X = pd.get_dummies(f[["family", "cell_class", "delivery", "evolved", "log_cargo"]],
99
+ columns=["family", "cell_class", "delivery"]).astype(float)
100
+ self.columns = list(X.columns)
101
+ self.model = _fit_gbr(X, df["efficiency_pct"].to_numpy(), seed)
102
+ self.meta = {"n": int(len(df)), "families": sorted(df["family"].unique()),
103
+ "efficiency_units": "percent_integration"}
104
+ return self
105
+
106
+ def _design_matrix(self, df: pd.DataFrame) -> pd.DataFrame:
107
+ f = featurize(df)
108
+ X = pd.get_dummies(f[["family", "cell_class", "delivery", "evolved", "log_cargo"]],
109
+ columns=["family", "cell_class", "delivery"]).astype(float)
110
+ return X.reindex(columns=self.columns, fill_value=0.0)
111
+
112
+ def predict(self, df: pd.DataFrame) -> np.ndarray:
113
+ return self.model.predict(self._design_matrix(df))
114
+
115
+ def predict_interval(self, df: pd.DataFrame) -> dict:
116
+ # Integration efficiency is a percentage, so the SERVED point estimate is clamped to the physical [0, 100]
117
+ # range (the regressor can extrapolate outside it on an out-of-distribution request), matching the interval
118
+ # bounds. The interval stays centred on the raw prediction, and `predict` is left unclamped so the
119
+ # pre-registered held-out evaluation scores the model exactly as trained.
120
+ yhat = self.predict(df)
121
+ if self.conformal is None or not np.isfinite(self.conformal.qhat):
122
+ return {"yhat": np.clip(yhat, 0, 100), "lo": None, "hi": None, "interval_kind": "uncalibrated"}
123
+ lo, hi = self.conformal.interval(yhat)
124
+ return {"yhat": np.clip(yhat, 0, 100), "lo": np.clip(lo, 0, 100), "hi": np.clip(hi, 0, 100),
125
+ "nominal_coverage": 1 - self.conformal.alpha,
126
+ "interval_kind": "trained split-conformal (% integration; candidate)"}
127
+
128
+ def save(self, path: str | Path) -> str:
129
+ path = Path(path)
130
+ path.parent.mkdir(parents=True, exist_ok=True)
131
+ with open(path, "wb") as fh:
132
+ pickle.dump({"columns": self.columns, "model": self.model, "conformal": self.conformal,
133
+ "meta": self.meta}, fh)
134
+ return str(path)
135
+
136
+ @classmethod
137
+ def load(cls, path: str | Path) -> "WriterEfficiencyModel":
138
+ with open(path, "rb") as fh:
139
+ return cls(**pickle.load(fh))
140
+
141
+
142
+ # evaluation: leave-one-family-out + leave-one-locus-out, learned vs KB family-mean baseline -----
143
+ def _loo_groups(df: pd.DataFrame, by: str):
144
+ vals = df[by].to_numpy()
145
+ for g in pd.unique(vals):
146
+ te = np.where(vals == g)[0]
147
+ tr = np.where(vals != g)[0]
148
+ if len(tr) >= 6 and len(te) >= 1:
149
+ yield g, tr, te
150
+
151
+
152
+ def _boot_delta(model_err, base_err, reps=2000, seed=20260620):
153
+ """Paired bootstrap 95% CI on mean(base_err) - mean(model_err) (positive = model better, lower error)."""
154
+ rng = np.random.default_rng(seed)
155
+ me, be = np.asarray(model_err, float), np.asarray(base_err, float)
156
+ n = len(me)
157
+ d = []
158
+ for _ in range(reps):
159
+ idx = rng.integers(0, n, n)
160
+ d.append(float(np.mean(be[idx]) - np.mean(me[idx])))
161
+ lo, hi = np.percentile(d, [2.5, 97.5])
162
+ return {"delta_mean_abs_err_reduction": float(np.mean(d)), "ci95": [float(lo), float(hi)],
163
+ "model_beats_baseline": bool(lo > 0)}
164
+
165
+
166
+ def evaluate(df: pd.DataFrame | None = None, seed: int = 42) -> dict:
167
+ """Held-out-family AND held-out-locus CV: learned model vs the KB family-mean baseline. Reports Spearman,
168
+ MAE, and the paired-bootstrap CI on the MAE reduction. The acceptance gate = beats baseline on BOTH axes (CI excludes 0)."""
169
+ df = human_cell(strict=False).reset_index(drop=True) if df is None else df.reset_index(drop=True)
170
+ out = {"n": int(len(df)), "n_families": int(df["family"].nunique())}
171
+ for axis, by, sub in (("held_out_family", "family", df),
172
+ ("held_out_locus", "locus", df[specific_locus_mask(df)].reset_index(drop=True))):
173
+ y_true, y_model, y_base = [], [], []
174
+ for _g, tr, te in _loo_groups(sub, by):
175
+ tr_df, te_df = sub.iloc[tr], sub.iloc[te]
176
+ f = featurize(tr_df)
177
+ Xtr = pd.get_dummies(f[["family", "cell_class", "delivery", "evolved", "log_cargo"]],
178
+ columns=["family", "cell_class", "delivery"]).astype(float)
179
+ m = _fit_gbr(Xtr, tr_df["efficiency_pct"].to_numpy(), seed)
180
+ fte = featurize(te_df)
181
+ Xte = pd.get_dummies(fte[["family", "cell_class", "delivery", "evolved", "log_cargo"]],
182
+ columns=["family", "cell_class", "delivery"]).reindex(columns=Xtr.columns,
183
+ fill_value=0.0).astype(float)
184
+ y_true.extend(te_df["efficiency_pct"].tolist())
185
+ y_model.extend(m.predict(Xte).tolist())
186
+ y_base.extend(_family_mean_baseline(tr_df, te_df).tolist())
187
+ y_true, y_model, y_base = map(np.array, (y_true, y_model, y_base))
188
+ if len(y_true) < 3:
189
+ out[axis] = {"available": False, "n": int(len(y_true))}
190
+ continue
191
+ me = np.abs(y_true - y_model)
192
+ be = np.abs(y_true - y_base)
193
+ out[axis] = {
194
+ "n": int(len(y_true)),
195
+ "mae_model": float(np.mean(me)), "mae_baseline_family_mean": float(np.mean(be)),
196
+ "spearman_model": float(spearmanr(y_model, y_true).statistic),
197
+ "spearman_baseline": float(spearmanr(y_base, y_true).statistic),
198
+ "delta": _boot_delta(me, be, seed=seed),
199
+ }
200
+ mf = out.get("held_out_family", {}).get("delta", {}).get("model_beats_baseline", False)
201
+ ml = out.get("held_out_locus", {}).get("delta", {}).get("model_beats_baseline", False)
202
+ out["gate_C_G2"] = {
203
+ "beats_on_held_out_family": bool(mf), "beats_on_held_out_locus": bool(ml),
204
+ "ship_learned_model": bool(mf and ml),
205
+ "verdict": ("learned predictor beats the KB family-mean baseline on BOTH held-out family AND locus, "
206
+ "serve it (candidate-flagged)" if (mf and ml) else
207
+ "learned predictor does NOT beat the KB baseline on both axes at this N, RETAIN the KB "
208
+ "ranking, report the negative; the curated dataset + Writer-Efficiency Bench are the "
209
+ "contribution (the pre-registered outcome)")}
210
+ return out
211
+
212
+
213
+ def calibrate(df: pd.DataFrame | None = None, alpha: float = 0.10, seed: int = 42) -> ConformalRegressor:
214
+ """Leave-one-family-out OOF residuals → split-conformal interval on the % scale (family-blocked)."""
215
+ df = human_cell(strict=False).reset_index(drop=True) if df is None else df.reset_index(drop=True)
216
+ y_true, y_hat = [], []
217
+ for _g, tr, te in _loo_groups(df, "family"):
218
+ tr_df, te_df = df.iloc[tr], df.iloc[te]
219
+ f = featurize(tr_df)
220
+ Xtr = pd.get_dummies(f[["family", "cell_class", "delivery", "evolved", "log_cargo"]],
221
+ columns=["family", "cell_class", "delivery"]).astype(float)
222
+ m = _fit_gbr(Xtr, tr_df["efficiency_pct"].to_numpy(), seed)
223
+ fte = featurize(te_df)
224
+ Xte = pd.get_dummies(fte[["family", "cell_class", "delivery", "evolved", "log_cargo"]],
225
+ columns=["family", "cell_class", "delivery"]).reindex(columns=Xtr.columns,
226
+ fill_value=0.0).astype(float)
227
+ y_true.extend(te_df["efficiency_pct"].tolist())
228
+ y_hat.extend(m.predict(Xte).tolist())
229
+ return ConformalRegressor(alpha=alpha).calibrate(np.array(y_true), np.array(y_hat))
@@ -0,0 +1,170 @@
1
+ """Writer recommender, the surface that ties writer selection together.
2
+
3
+ Given a write request `(write-type, cargo size, cell type, optional target/donor sequences)`, returns ranked
4
+ writer families with:
5
+ * the **KB readiness** (PRIMARY ranking, the curated Writer Atlas; retained per the pre-registered gate),
6
+ * a **predicted efficiency + conformal interval**, candidate-flagged (the learned predictor, an advisory, NOT
7
+ the authoritative ranking, because at N=42/4-families it does not beat the KB baseline on held-out family),
8
+ * **cargo-capacity fit** (does the family carry this cargo?),
9
+ * an **auto-designed guide** (bridge RNA / pegRNA+attB) when sequences are supplied.
10
+
11
+ Scope: efficiency predictions are candidates with intervals; the KB ranking is the grounded primary. No
12
+ fabricated efficiency is ever emitted (the predictor is trained only on the curated real dataset).
13
+ """
14
+ from __future__ import annotations
15
+
16
+ from functools import lru_cache
17
+
18
+ import pandas as pd
19
+
20
+ from pen_stack._resources import project_root
21
+
22
+
23
+ @lru_cache(maxsize=1)
24
+ def _wtkb() -> pd.DataFrame:
25
+ return pd.read_parquet(project_root() / "pen_stack/atlas/wtkb.parquet")
26
+
27
+
28
+ @lru_cache(maxsize=1)
29
+ def _eff_model():
30
+ """Load the learned efficiency model artifact if present; else None (KB-only recommendation)."""
31
+ from pen_stack.atlas.writer_predict import WriterEfficiencyModel
32
+ p = project_root() / "models/writer_eff.pkl"
33
+ try:
34
+ return WriterEfficiencyModel.load(p) if p.exists() else None
35
+ except Exception: # noqa: BLE001
36
+ return None
37
+
38
+
39
+ def _kb_readiness(row) -> float:
40
+ """A transparent KB readiness in [0,1] from the curated atlas fields: DSB-free (writing-relevant) + measured
41
+ human-cell activity + cargo headroom. PRIMARY ranking signal (grounded, not learned)."""
42
+ dsb_free = 1.0 if bool(row.get("dsb_free")) else 0.0
43
+ measured = 1.0 if str(row.get("confidence")) == "measured" else (0.5 if str(row.get("confidence")) == "inferred" else 0.25)
44
+ act = str(row.get("human_cell_activity") or "").lower()
45
+ human = 1.0 if ("%" in act or "high" in act) and "not measured" not in act and "low" not in act else (
46
+ 0.3 if "low" in act or "not measured" in act else 0.6)
47
+ return round(0.4 * dsb_free + 0.35 * measured + 0.25 * human, 3)
48
+
49
+
50
+ # Mechanism-grounded write-type suitability. A cargo write (insertion / landing-pad) NEEDS a cargo-carrying writer;
51
+ # an excision NEEDS a programmable cutter. This is molecular MECHANISM (a cargo installer vs a cutter), NOT an
52
+ # efficacy prediction -- a transparent, labelled tier so the write-type selector actually shapes the ranking.
53
+ _CARGO_WRITES = frozenset({"insertion", "knock_in_with_disruption", "landing_pad_insertion", "high_durability_insertion"})
54
+ _EXCISION_WRITES = frozenset({"excision", "repeat_excision", "regulatory_element_excision", "regulatory_excision"})
55
+ WRITE_TYPES = _CARGO_WRITES | _EXCISION_WRITES | frozenset({"inversion"})
56
+
57
+
58
+ # Families that catalyse site-specific INVERSION (reversible recombination between two inverted sites): the serine
59
+ # integrases (invert between inverted att) and the IS110/IS1111 bridge recombinases (Durrant 2024); a PE-installed
60
+ # att inherits the serine mechanism. The CAST transposase is unidirectional INSERTION only, and DSB nucleases invert
61
+ # only imprecisely via dual cuts -- so those are not the mechanism for a clean inversion.
62
+ _INVERSION_RECOMBINASES = frozenset({"serine_integrase", "PE_integrase", "bridge_IS110", "seek_IS1111"})
63
+
64
+
65
+ def _write_type_suitability(write_type: str, family: str, carries_cargo: bool) -> int:
66
+ """Mechanism suitability TIER (1 = the right mechanism class for this write type, 0 = a poorer fit). Grounded
67
+ in mechanism, never an efficacy claim: a cargo write needs a cargo carrier; an excision needs a programmable
68
+ cutter; an inversion needs a site-specific recombinase."""
69
+ if write_type in _CARGO_WRITES:
70
+ return 1 if carries_cargo else 0 # nucleases can knock-in via HDR but do not deliver large cargo
71
+ if write_type in _EXCISION_WRITES:
72
+ return 0 if carries_cargo else 1 # a cut/excise favours a programmable cutter, not an installer
73
+ if write_type == "inversion":
74
+ return 1 if family in _INVERSION_RECOMBINASES else 0 # only site-specific recombinases invert cleanly
75
+ return 1 # other: neutral (no grounded reweight)
76
+
77
+
78
+ def recommend_writers(request: dict, top_k: int = 5) -> dict:
79
+ """Rank writer families for a write request. KB readiness is primary; learned efficiency is a candidate
80
+ advisory; guide design attached when sequences are supplied."""
81
+ cargo_bp = float(request.get("cargo_bp") or 0)
82
+ cell_type = request.get("cell_type") or "HEK293T"
83
+ # `write_type` is the documented key; `intent` is accepted as a synonym because the planner CLI names the same
84
+ # idea `--intent`, so a request that uses it is honoured instead of silently falling back to the default
85
+ # insertion. An unrecognized value still ranks neutrally, but says so via `write_type_recognized` below rather
86
+ # than looking like a shaped ranking.
87
+ write_type = request.get("write_type") or request.get("intent") or "insertion"
88
+ target_seq, donor_seq = request.get("target_seq"), request.get("donor_seq")
89
+ model = _eff_model()
90
+ rows = []
91
+ for _, r in _wtkb().iterrows():
92
+ cap = float(r["cargo_capacity_bp"]) if pd.notna(r.get("cargo_capacity_bp")) else 0.0
93
+ cargo_fit = (cap >= cargo_bp) if (cargo_bp and cap) else None
94
+ rec = {
95
+ "family": r["family"], "representative_system": r["representative_system"],
96
+ "kb_readiness": _kb_readiness(r), "dsb_free": bool(r.get("dsb_free")),
97
+ "write_type_suitability": _write_type_suitability(write_type, r["family"], cap > 0), # mechanism fit
98
+ "cargo_capacity_bp": int(cap) if cap else None,
99
+ "cargo_fit": cargo_fit, "confidence": str(r.get("confidence")),
100
+ "key_dois": [str(x) for x in r.get("key_dois")] if r.get("key_dois") is not None else [],
101
+ }
102
+ # learned efficiency (candidate advisory), ONLY for families present in the curated dataset (never
103
+ # extrapolate an efficiency to an unseen family; KB-only for those, no fabrication).
104
+ if model is not None and r["family"] in model.meta.get("families", []):
105
+ try:
106
+ df = pd.DataFrame([{"family": r["family"], "variant": "engineered",
107
+ "cargo_bp": max(cargo_bp, 1000), "cell_type": cell_type,
108
+ "delivery": "plasmid"}])
109
+ iv = model.predict_interval(df)
110
+ if iv.get("lo") is not None:
111
+ rec["predicted_efficiency_pct"] = round(float(iv["yhat"][0]), 1)
112
+ rec["efficiency_interval_pct"] = [round(float(iv["lo"][0]), 1), round(float(iv["hi"][0]), 1)]
113
+ rec["efficiency_kind"] = "candidate (learned; interval = trained split-conformal)"
114
+ except Exception: # noqa: BLE001 - family unseen by the model -> KB-only, never fabricate
115
+ pass
116
+ # guide design when sequences supplied
117
+ if target_seq:
118
+ from pen_stack.atlas.guide_design import design_guide_for_writer
119
+ g = design_guide_for_writer(r["family"], target_seq, donor_seq)
120
+ if g.get("available"):
121
+ # surface the DESIGNED sequences (spacer / attB / bridge loops), not just a bare flag
122
+ rec["guide_design"] = {"type": g["design_type"], "feasible": g.get("feasible"),
123
+ "design": g.get("design")}
124
+ rows.append(rec)
125
+
126
+ # rank by mechanism suitability for the write type FIRST (grounded tier), then the grounded KB readiness within
127
+ # tier, then cargo fit. For the default insertion this is unchanged (cargo carriers already lead); an excision
128
+ # promotes programmable cutters over cargo installers.
129
+ rows.sort(key=lambda x: (x["write_type_suitability"], x["kb_readiness"], x.get("cargo_fit") is True), reverse=True)
130
+ if cargo_bp:
131
+ rows = [x for x in rows if x["cargo_fit"] is not False] + [x for x in rows if x["cargo_fit"] is False]
132
+ wt_note = None
133
+ if write_type in _EXCISION_WRITES:
134
+ wt_note = (f"Ranking shaped for a {write_type.replace('_', ' ')}: programmable cutters are favoured over "
135
+ "cargo-installing integrases, which are the tool for insertion, not excision. Mechanism-based "
136
+ "modifier, not an efficacy claim.")
137
+ elif write_type in _CARGO_WRITES:
138
+ wt_note = (f"Ranking shaped for a {write_type.replace('_', ' ')}: cargo-carrying writers (integrases / "
139
+ "transposons) are favoured; nucleases can knock in via HDR but do not deliver a large cargo.")
140
+ elif write_type == "inversion":
141
+ wt_note = ("Ranking shaped for an inversion: site-specific recombinases (serine integrases, IS110/IS1111 "
142
+ "bridge recombinases) that invert between inverted sites are favoured; the CAST transposase is "
143
+ "insertion-only and DSB nucleases invert only imprecisely via dual cuts. Mechanism-based "
144
+ "modifier, not an efficacy claim.")
145
+ # Cargo-capacity check: when a cargo is requested that NO cargo-carrying writer in the atlas can hold, the
146
+ # top of the ranking is nucleases (which don't deliver cargo, cargo_fit=n/a) or over-capacity integrases --
147
+ # surface that explicitly so Cas9 does not read as a silent #1 recommendation for an impossible payload.
148
+ cargo_warning = None
149
+ if cargo_bp:
150
+ caps = [x["cargo_capacity_bp"] for x in rows if x["cargo_capacity_bp"]]
151
+ max_cap = max(caps) if caps else 0
152
+ if max_cap and cargo_bp > max_cap:
153
+ cargo_warning = (
154
+ f"No writer in the atlas carries a {int(cargo_bp):,} bp cargo -- the largest cargo capacity is "
155
+ f"{int(max_cap):,} bp. Families marked 'over capacity' exceed their limit; nucleases (cargo fit "
156
+ f"n/a) do not deliver cargo at all. Consider splitting the payload or a dual-vector strategy.")
157
+ return {
158
+ "request": {"write_type": write_type, "write_type_recognized": write_type in WRITE_TYPES,
159
+ "cargo_bp": cargo_bp or None, "cell_type": cell_type},
160
+ "ranking_basis": "mechanism suitability for the write type, then KB readiness (grounded); predicted "
161
+ "efficiency is a candidate advisory",
162
+ "recommendations": rows[:top_k],
163
+ "n_families": len(rows),
164
+ "write_type_note": wt_note,
165
+ "cargo_capacity_warning": cargo_warning,
166
+ "no_fabrication": True,
167
+ "note": "Writer Atlas KB ranking is the grounded primary; learned efficiencies are candidates with "
168
+ "conformal intervals (the learned model does not beat the KB baseline on held-out family "
169
+ "at this N). Guide designs are candidates requiring empirical validation.",
170
+ }