Source code for openprotein.app.models.designer
from typing import Generator
from openprotein.api import assaydata, designer
from openprotein.api import job as job_api
from openprotein.base import APISession
from openprotein.schemas import Criteria, Design, DesignJob
from openprotein.schemas.designer import DesignAlgorithm
from .assaydata import AssayDataset
from .futures import Future, StreamingFuture
[docs]
class DesignFuture(StreamingFuture, Future):
"""A future object that will hold the results of the design job."""
job: DesignJob
[docs]
def __init__(
self,
session: APISession,
job: DesignJob | None = None,
metadata: Design | None = None,
):
"""
Construct a future for a design job.
Takes in either a design job, or the design metadata.
:meta private:
"""
self._design_assay = None
if metadata is None:
if job is None:
raise ValueError("Expected design metadata or job")
metadata = designer.design_get(session=session, design_id=job.job_id)
self._metadata = metadata
if job is None:
job = DesignJob.create(job_api.job_get(session=session, job_id=metadata.id))
super().__init__(session, job)
@property
def id(self):
"""ID of the design."""
return self._metadata.id
@property
def assay(self) -> AssayDataset:
"""Assay used in the design."""
if self._design_assay is None:
self._design_assay = self.get_assay()
return self._design_assay
@property
def algorithm(self) -> DesignAlgorithm:
"""Algorithm used in the design."""
return self._metadata.algorithm
@property
def criteria(self) -> Criteria:
"""Criteria used in the design."""
return self._metadata.criteria
@property
def num_steps(self):
"""Number of steps used in the design."""
return self._metadata.num_steps
@property
def num_rows(self):
"""Number of rows in the total design output (across all steps)."""
return self._metadata.num_rows
@property
def allowed_tokens(self) -> dict[str, list[str]] | None:
"""Allowed tokens used in the design."""
return self._metadata.allowed_tokens
@property
def pop_size(self) -> int:
"""Population size used in the design."""
return self._metadata.pop_size
@property
def n_offsprings(self) -> int:
"""Number of offsprings used in the design."""
return self._metadata.n_offsprings
@property
def crossover_prob(self) -> float:
"""Crossover probability used in the design."""
return self._metadata.crossover_prob
@property
def crossover_prob_pointwise(self) -> float:
"""Crossover probability pointwise used in the design."""
return self._metadata.crossover_prob_pointwise
@property
def mutation_average_mutations_per_seq(self) -> int:
"""Average mutations per sequence used in the design."""
return self._metadata.mutation_average_mutations_per_seq
@property
def metadata(self):
"""Design metadata."""
self._refresh_metadata()
return self._metadata
def _refresh_metadata(self):
if not self._metadata.is_done():
self._metadata = designer.design_get(
session=self.session, design_id=self._metadata.id
)
def __delete(self) -> bool:
"""
Delete this design.
TODO - implementation
"""
return designer.design_delete(session=self.session, design_id=self.id)
[docs]
def stream(self, step: int | None = None) -> Generator:
stream = designer.designer_get_design_results(
session=self.session, design_id=self.id, step=step
)
return designer.decode_design_results_stream(data=stream)
[docs]
def get_assay(self) -> AssayDataset:
"""
Get assay used for design job.
Returns
-------
AssayDataset
Assay dataset used for design.
"""
return AssayDataset(
session=self.session,
metadata=assaydata.get_assay_metadata(
self.session, self._metadata.assay_id
),
)