Skip to content

Commit 237b0be

Browse files
committed
Release v0.8.9
- Add reading scores for RF3
1 parent f2eb03a commit 237b0be

File tree

8 files changed

+542
-140
lines changed

8 files changed

+542
-140
lines changed

.gitignore

Lines changed: 221 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
# SECRETS FILE
33
secrets.config
44
poetry.lock
5-
/.github/workflows/s3.yaml
65
/htmlcov
76
/apidocs/build
87
# VS code
@@ -143,3 +142,224 @@ poet_demo.ipynb
143142
*chorismate_mutase_combined_seqs.fasta
144143

145144
nohup.out
145+
# pixi environments
146+
.pixi
147+
*.egg-info
148+
149+
notebooks
150+
./data/
151+
.aider*
152+
153+
build
154+
# Python
155+
# Byte-compiled / optimized / DLL files
156+
__pycache__/
157+
*.py[codz]
158+
*$py.class
159+
160+
# C extensions
161+
*.so
162+
163+
# Distribution / packaging
164+
.Python
165+
build/
166+
develop-eggs/
167+
dist/
168+
downloads/
169+
eggs/
170+
.eggs/
171+
lib/
172+
lib64/
173+
parts/
174+
sdist/
175+
var/
176+
wheels/
177+
share/python-wheels/
178+
*.egg-info/
179+
.installed.cfg
180+
*.egg
181+
MANIFEST
182+
183+
# PyInstaller
184+
# Usually these files are written by a python script from a template
185+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
186+
*.manifest
187+
*.spec
188+
189+
# Installer logs
190+
pip-log.txt
191+
pip-delete-this-directory.txt
192+
193+
# Unit test / coverage reports
194+
htmlcov/
195+
.tox/
196+
.nox/
197+
.coverage
198+
.coverage.*
199+
.cache
200+
nosetests.xml
201+
coverage.xml
202+
*.cover
203+
*.py.cover
204+
.hypothesis/
205+
.pytest_cache/
206+
cover/
207+
208+
# Translations
209+
*.mo
210+
*.pot
211+
212+
# Django stuff:
213+
*.log
214+
local_settings.py
215+
db.sqlite3
216+
db.sqlite3-journal
217+
218+
# Flask stuff:
219+
instance/
220+
.webassets-cache
221+
222+
# Scrapy stuff:
223+
.scrapy
224+
225+
# Sphinx documentation
226+
docs/_build/
227+
228+
# PyBuilder
229+
.pybuilder/
230+
target/
231+
232+
# Jupyter Notebook
233+
.ipynb_checkpoints
234+
235+
# IPython
236+
profile_default/
237+
ipython_config.py
238+
239+
# pyenv
240+
# For a library or package, you might want to ignore these files since the code is
241+
# intended to run in multiple environments; otherwise, check them in:
242+
# .python-version
243+
244+
# pipenv
245+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
246+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
247+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
248+
# install all needed dependencies.
249+
#Pipfile.lock
250+
251+
# UV
252+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
253+
# This is especially recommended for binary packages to ensure reproducibility, and is more
254+
# commonly ignored for libraries.
255+
#uv.lock
256+
257+
# poetry
258+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
259+
# This is especially recommended for binary packages to ensure reproducibility, and is more
260+
# commonly ignored for libraries.
261+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
262+
#poetry.lock
263+
#poetry.toml
264+
265+
# pdm
266+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
267+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
268+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
269+
#pdm.lock
270+
#pdm.toml
271+
.pdm-python
272+
.pdm-build/
273+
274+
# pixi
275+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
276+
#pixi.lock
277+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
278+
# in the .venv directory. It is recommended not to include this directory in version control.
279+
.pixi
280+
281+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
282+
__pypackages__/
283+
284+
# Celery stuff
285+
celerybeat-schedule
286+
celerybeat.pid
287+
288+
# SageMath parsed files
289+
*.sage.py
290+
291+
# Environments
292+
.env
293+
.envrc
294+
.venv
295+
env/
296+
venv/
297+
ENV/
298+
env.bak/
299+
venv.bak/
300+
301+
# Spyder project settings
302+
.spyderproject
303+
.spyproject
304+
305+
# Rope project settings
306+
.ropeproject
307+
308+
# mkdocs documentation
309+
/site
310+
311+
# mypy
312+
.mypy_cache/
313+
.dmypy.json
314+
dmypy.json
315+
316+
# Pyre type checker
317+
.pyre/
318+
319+
# pytype static type analyzer
320+
.pytype/
321+
322+
# Cython debug symbols
323+
cython_debug/
324+
325+
# PyCharm
326+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
327+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
328+
# and can be added to the global gitignore or merged into this file. For a more nuclear
329+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
330+
#.idea/
331+
332+
# Abstra
333+
# Abstra is an AI-powered process automation framework.
334+
# Ignore directories containing user credentials, local state, and settings.
335+
# Learn more at https://abstra.io/docs
336+
.abstra/
337+
338+
# Visual Studio Code
339+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
340+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
341+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
342+
# you could uncomment the following to ignore the entire vscode folder
343+
# .vscode/
344+
345+
# Ruff stuff:
346+
.ruff_cache/
347+
348+
# PyPI configuration file
349+
.pypirc
350+
351+
# Cursor
352+
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
353+
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
354+
# refer to https://docs.cursor.com/context/ignore-files
355+
.cursorignore
356+
.cursorindexingignore
357+
358+
# Marimo
359+
marimo/_static/
360+
marimo/_lsp/
361+
__marimo__/
362+
363+
# Streamlit
364+
.streamlit/secrets.toml
365+
/data/

openprotein/fold/api.py

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Fold REST API interface for making HTTP calls to our fold backend."""
22

33
import io
4-
from typing import Literal
4+
from typing import TYPE_CHECKING, Literal
55

66
import numpy as np
77
from pydantic import TypeAdapter
@@ -12,6 +12,9 @@
1212

1313
from .schemas import FoldJob, FoldMetadata
1414

15+
if TYPE_CHECKING:
16+
import pandas as pd
17+
1518
PATH_PREFIX = "v1/fold"
1619

1720

@@ -160,8 +163,8 @@ def fold_get_complex_result(
160163
def fold_get_complex_extra_result(
161164
session: APISession,
162165
job_id: str,
163-
key: Literal["pae", "pde", "plddt", "confidence", "affinity"],
164-
) -> np.ndarray | list[dict]:
166+
key: Literal["pae", "pde", "plddt", "confidence", "affinity", "score", "metrics"],
167+
) -> "np.ndarray | list[dict] | pd.DataFrame":
165168
"""
166169
Get extra result for a complex from the request ID.
167170
@@ -183,6 +186,10 @@ def fold_get_complex_extra_result(
183186
formatter = lambda response: np.load(io.BytesIO(response.content))
184187
elif key in {"confidence", "affinity"}:
185188
formatter = lambda response: response.json()
189+
elif key in {"score", "metrics"}:
190+
import pandas as pd
191+
192+
formatter = lambda response: pd.read_csv(io.StringIO(response.content.decode()))
186193
else:
187194
raise ValueError(f"Unexpected key: {key}")
188195
endpoint = PATH_PREFIX + f"/{job_id}/complex/{key}"
@@ -194,7 +201,7 @@ def fold_get_complex_extra_result(
194201
if e.status_code == 400 and key == "affinity":
195202
raise ValueError("affinity not found for request") from None
196203
raise e
197-
output: np.ndarray | list[dict] = formatter(response)
204+
output = formatter(response)
198205
return output
199206

200207

@@ -254,34 +261,11 @@ def fold_models_post(
254261
sequences = kwargs["sequences"]
255262
# NOTE we are handling the boltz form here too
256263
sequences = [s.decode() if isinstance(s, bytes) else s for s in sequences]
257-
body["sequences"] = sequences
258-
if kwargs.get("msa_id"):
259-
body["msa_id"] = kwargs["msa_id"]
260-
if kwargs.get("num_recycles"):
261-
body["num_recycles"] = kwargs["num_recycles"]
262-
if kwargs.get("num_models"):
263-
body["num_models"] = kwargs["num_models"]
264-
if kwargs.get("num_relax"):
265-
body["num_relax"] = kwargs["num_relax"]
266-
if kwargs.get("use_potentials"):
267-
body["use_potentials"] = kwargs["use_potentials"]
268-
# boltz
269-
if kwargs.get("diffusion_samples"):
270-
body["diffusion_samples"] = kwargs["diffusion_samples"]
271-
if kwargs.get("recycling_steps"):
272-
body["recycling_steps"] = kwargs["recycling_steps"]
273-
if kwargs.get("sampling_steps"):
274-
body["sampling_steps"] = kwargs["sampling_steps"]
275-
if kwargs.get("step_scale"):
276-
body["step_scale"] = kwargs["step_scale"]
277-
if kwargs.get("constraints"):
278-
body["constraints"] = kwargs["constraints"]
279-
if kwargs.get("templates"):
280-
body["templates"] = kwargs["templates"]
281-
if kwargs.get("properties"):
282-
body["properties"] = kwargs["properties"]
283-
if kwargs.get("method"):
284-
body["method"] = kwargs["method"]
264+
kwargs["sequences"] = sequences
265+
# add non-None args - note this doesnt affect msa_id which is nested
266+
for k, v in kwargs.items():
267+
if v is not None:
268+
body[k] = v
285269

286270
response = session.post(endpoint, json=body)
287271
return FoldJob.model_validate(response.json())

openprotein/fold/future.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from typing import TYPE_CHECKING, Literal
44

55
import numpy as np
6+
import pandas as pd
67
from pydantic.type_adapter import TypeAdapter
78
from typing_extensions import Self
89

@@ -246,6 +247,8 @@ def __init__(
246247
self._pae: np.ndarray | None = None
247248
self._pde: np.ndarray | None = None
248249
self._plddt: np.ndarray | None = None
250+
self._score: pd.DataFrame | None = None
251+
self._metrics: pd.DataFrame | None = None
249252
self._confidence: list["BoltzConfidence"] | None = None
250253
self._affinity: "BoltzAffinity | None" = None
251254

@@ -439,6 +442,56 @@ def plddt(self) -> np.ndarray:
439442
self._plddt = plddt
440443
return self._plddt
441444

445+
@property
446+
def score(self) -> pd.DataFrame:
447+
"""
448+
Get the predicted scores.
449+
450+
Returns
451+
-------
452+
pd.DataFrame
453+
Structure prediction scores.
454+
455+
Raises
456+
------
457+
AttributeError
458+
If score is not supported for the model.
459+
"""
460+
if self.model_id not in {"rosettafold-3"}:
461+
raise AttributeError("score not supported for non-RosettaFold model")
462+
if self._score is None:
463+
score = api.fold_get_complex_extra_result(
464+
session=self.session, job_id=self.job.job_id, key="score"
465+
)
466+
assert isinstance(score, pd.DataFrame)
467+
self._score = score
468+
return self._score
469+
470+
@property
471+
def metrics(self) -> pd.DataFrame:
472+
"""
473+
Get the predicted metrics.
474+
475+
Returns
476+
-------
477+
pd.DataFrame
478+
Structure prediction metrics.
479+
480+
Raises
481+
------
482+
AttributeError
483+
If metrics is not supported for the model.
484+
"""
485+
if self.model_id not in {"rosettafold-3"}:
486+
raise AttributeError("metrics not supported for non-RosettaFold model")
487+
if self._metrics is None:
488+
metrics = api.fold_get_complex_extra_result(
489+
session=self.session, job_id=self.job.job_id, key="metrics"
490+
)
491+
assert isinstance(metrics, pd.DataFrame)
492+
self._metrics = metrics
493+
return self._metrics
494+
442495
@property
443496
def confidence(self) -> list["BoltzConfidence"]:
444497
"""

0 commit comments

Comments
 (0)