ferritin_plms/ligandmpnn/
pmpnn_runner.rs1use super::configs::ProteinMPNNConfig;
5use super::model::ProteinMPNN;
6use super::proteinfeatures::ProteinFeatures;
7use crate::types::PseudoProbability;
8use anyhow::{Result, anyhow};
9use candle_core::pickle::PthTensors;
10use candle_core::{DType, Device, Tensor};
11use candle_nn::VarBuilder;
12use hf_hub::HFClientSync;
13use std::path::Path;
14
15const PMPNN_DTYPE: DType = DType::F32;
16
17pub enum ProteinMPNNModels {
18 V48_020,
20}
21
22impl ProteinMPNNModels {
23 fn hf_info(&self) -> (&'static str, &'static str, &'static str, &'static str) {
24 match self {
26 Self::V48_020 => (
27 "zcpbx",
28 "ligandmpnn-weights",
29 "main",
30 "model_params/proteinmpnn_v_48_020.pt",
31 ),
32 }
33 }
34}
35
36pub struct ProteinMPNNRunner {
37 model: ProteinMPNN,
38}
39
40impl ProteinMPNNRunner {
41 pub fn load_model(modeltype: ProteinMPNNModels, device: Device) -> Result<Self> {
43 let (owner, repo, revision, filename) = modeltype.hf_info();
44 let client = HFClientSync::new()?;
45 let hf_repo = client.model(owner, repo);
46 let weights_path = hf_repo
47 .download_file()
48 .filename(filename)
49 .revision(revision)
50 .send()
51 .map_err(|e| anyhow!("Failed to download ProteinMPNN weights from HF hub: {e}"))?;
52 Self::from_path(&weights_path, device)
53 }
54
55 pub fn from_path(path: impl AsRef<Path>, device: Device) -> Result<Self> {
57 let path = path.as_ref();
58 let pth = PthTensors::new(path, Some("model_state_dict"))
59 .map_err(|e| anyhow!("Failed to open {}: {e}", path.display()))?;
60 let vb = VarBuilder::from_backend(Box::new(pth), PMPNN_DTYPE, device);
61 let config = ProteinMPNNConfig::proteinmpnn();
62 let model = ProteinMPNN::load(vb, &config)
63 .map_err(|e| anyhow!("Failed to load ProteinMPNN weights: {e}"))?;
64 Ok(Self { model })
65 }
66
67 pub fn get_log_probs(&self, features: &ProteinFeatures) -> Result<Tensor> {
73 let output = self
74 .model
75 .simple_decode(features)
76 .map_err(|e| anyhow!("ProteinMPNN forward pass failed: {e}"))?;
77 output
79 .get_log_probs()
80 .squeeze(0)
81 .map_err(|e| anyhow!("Failed to squeeze batch dimension: {e}"))
82 }
83
84 pub fn get_pseudo_probabilities(
86 &self,
87 features: &ProteinFeatures,
88 ) -> Result<Vec<PseudoProbability>> {
89 let output = self
90 .model
91 .simple_decode(features)
92 .map_err(|e| anyhow!("ProteinMPNN forward pass failed: {e}"))?;
93 output
94 .get_pseudo_probabilities()
95 .map_err(|e| anyhow!("Failed to extract pseudo-probabilities: {e}"))
96 }
97}