ferritin_plms/ligandmpnn/
pmpnn_runner.rs1use super::configs::ProteinMPNNConfig;
5use super::model::ProteinMPNN;
6use super::proteinfeatures::ProteinFeatures;
7use crate::loader::{Format, LoadOptions, WeightSource, var_builder_from_path};
8use crate::registry::{self, ModelCard};
9use crate::types::PseudoProbability;
10use anyhow::{Result, anyhow};
11use candle_core::{Device, Tensor};
12use std::path::Path;
13
14const PMPNN_FORMAT: Format = Format::Pth {
16 root_key: Some("model_state_dict"),
17};
18
19pub enum ProteinMPNNModels {
20 V48_020,
22 LigandV32_020_25,
27}
28
29impl ProteinMPNNModels {
30 pub const fn registry_id(&self) -> &'static str {
32 match self {
33 Self::V48_020 => "proteinmpnn-v48-020",
34 Self::LigandV32_020_25 => "ligandmpnn-v32-020-25",
35 }
36 }
37
38 pub fn config(&self) -> ProteinMPNNConfig {
45 match self {
46 Self::V48_020 => ProteinMPNNConfig::proteinmpnn(),
47 Self::LigandV32_020_25 => ProteinMPNNConfig::ligandmpnn(),
48 }
49 }
50
51 pub fn card(&self) -> &'static ModelCard {
53 registry::lookup(self.registry_id())
54 .expect("every ProteinMPNNModels variant must have a registry entry")
55 }
56
57 pub fn model_info(&self) -> (WeightSource, &'static str) {
61 let card = self.card();
62 (card.source, card.file)
63 }
64
65 #[deprecated(since = "0.4.0", note = "renamed to `model_info`")]
67 pub fn hf_info(&self) -> (WeightSource, &'static str) {
68 self.model_info()
69 }
70}
71
72pub struct ProteinMPNNRunner {
73 model: ProteinMPNN,
74}
75
76impl ProteinMPNNRunner {
77 pub fn from_pretrained(modeltype: ProteinMPNNModels, device: Device) -> Result<Self> {
79 Self::from_pretrained_with(modeltype, &LoadOptions::new(device))
80 }
81
82 #[deprecated(since = "0.4.0", note = "renamed to `from_pretrained`")]
84 pub fn load_model(modeltype: ProteinMPNNModels, device: Device) -> Result<Self> {
85 Self::from_pretrained(modeltype, device)
86 }
87
88 #[deprecated(since = "0.4.0", note = "renamed to `from_pretrained_with`")]
90 pub fn load_model_with(modeltype: ProteinMPNNModels, opts: &LoadOptions) -> Result<Self> {
91 Self::from_pretrained_with(modeltype, opts)
92 }
93
94 pub fn from_pretrained_with(modeltype: ProteinMPNNModels, opts: &LoadOptions) -> Result<Self> {
96 let (source, filename) = modeltype.model_info();
97 let weights_path = source.fetch(filename)?;
98 Self::from_path_as(&weights_path, &modeltype.config(), opts)
99 }
100
101 pub fn from_path(path: impl AsRef<Path>, device: Device) -> Result<Self> {
108 Self::from_path_with(path, &LoadOptions::new(device))
109 }
110
111 pub fn from_path_with(path: impl AsRef<Path>, opts: &LoadOptions) -> Result<Self> {
113 Self::from_path_as(path, &ProteinMPNNConfig::proteinmpnn(), opts)
114 }
115
116 pub fn from_path_as(
118 path: impl AsRef<Path>,
119 config: &ProteinMPNNConfig,
120 opts: &LoadOptions,
121 ) -> Result<Self> {
122 let path = path.as_ref();
123 let vb = var_builder_from_path(path, PMPNN_FORMAT, opts)?;
124 let model = ProteinMPNN::load(vb, config)
125 .map_err(|e| anyhow!("Failed to load MPNN weights from {}: {e}", path.display()))?;
126 Ok(Self { model })
127 }
128
129 pub fn into_model(self) -> ProteinMPNN {
135 self.model
136 }
137
138 pub fn get_log_probs(&self, features: &ProteinFeatures) -> Result<Tensor> {
144 let output = self
145 .model
146 .simple_decode(features)
147 .map_err(|e| anyhow!("ProteinMPNN forward pass failed: {e}"))?;
148 output
150 .get_log_probs()
151 .squeeze(0)
152 .map_err(|e| anyhow!("Failed to squeeze batch dimension: {e}"))
153 }
154
155 pub fn get_pseudo_probabilities(
157 &self,
158 features: &ProteinFeatures,
159 ) -> Result<Vec<PseudoProbability>> {
160 let output = self
161 .model
162 .simple_decode(features)
163 .map_err(|e| anyhow!("ProteinMPNN forward pass failed: {e}"))?;
164 output
165 .get_pseudo_probabilities()
166 .map_err(|e| anyhow!("Failed to extract pseudo-probabilities: {e}"))
167 }
168}