Skip to content

Commit

Permalink
Merge pull request #10 from vivarium-collective/updates
Browse files Browse the repository at this point in the history
chore: updates to enable processes
  • Loading branch information
AlexPatrie committed May 7, 2024
2 parents 647e35a + 4935fce commit aa28d71
Show file tree
Hide file tree
Showing 88 changed files with 11,482 additions and 3,189 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,6 @@ bigraph-builder
3.10/
Miniforge3-Darwin-arm64.sh
*.cpython-310.pyc

playground.ipynb
composer-notebooks/playground.ipynb
*model_dir/
543 changes: 0 additions & 543 deletions ArchiveEditor.ipynb

This file was deleted.

8 changes: 4 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ WORKDIR /app

# copy and make dirs
COPY ./biosimulator_processes /app/biosimulator_processes
COPY notebooks /app/notebooks
COPY composer-notebooks /app/notebooks

# copy files
COPY ./pyproject.toml ./poetry.lock ./data ./scripts/trust-notebooks.sh /app/
Expand Down Expand Up @@ -51,10 +51,10 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
&& poetry config virtualenvs.in-project true \
&& poetry update \
&& poetry install \
&& chmod +x ./trust-notebooks.sh \
&& chmod +x ./trust-composer-notebooks.sh \
&& chmod +x /usr/local/bin/enter-lab.sh \
&& ./trust-notebooks.sh \
&& rm ./trust-notebooks.sh \
&& ./trust-composer-notebooks.sh \
&& rm ./trust-composer-notebooks.sh \
&& apt-get clean \
&& apt-get autoclean

Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ a jupyter notebook. The installation for this notebook is provided below.
### Using `biosimulator_processes.smoldyn_process.SmoldynProcess()`:

#### Mac Users PLEASE NOTE:

##### **Amici**
You most likely have to install/update `swig` with `brew`, among other possible requirements. Please refer to the
[Amici Python Installation Documentation](https://amici.readthedocs.io/en/latest/python_installation.html) for
more information.

##### **Smoldyn**
Due to the multi-lingual nature of Smoldyn, which is primarily
developed in C++, the installation process for utilizing
the `SmoldynProcess` process implementation requires separate handling. This is particularly
Expand Down
53 changes: 20 additions & 33 deletions biosimulator_processes/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
from builder import ProcessTypes
import importlib
from biosimulator_processes.data_model import SedDataModel
from typing import *

from builder import ProcessTypes
from bigraph_schema import TypeSystem
from process_bigraph import Composite

from biosimulator_processes.steps.viz import CompositionPlotter, Plotter2d
from biosimulator_processes.data_model.sed_data_model import MODEL_TYPE
from biosimulator_processes.utils import register_module


# Define a list of processes to attempt to import and register
Expand All @@ -9,37 +16,17 @@
('copasi', 'copasi_process.CopasiProcess'),
('smoldyn', 'smoldyn_process.SmoldynProcess'),
('tellurium', 'tellurium_process.TelluriumProcess'),
# ('parameter_scan', 'parameter_scan.DeterministicTimeCourseParameterScan')
]

CORE = ProcessTypes()
('amici', 'amici_process.AmiciProcess')]

for process_name, process_path in PROCESSES_TO_REGISTER:
module_name, class_name = process_path.rsplit('.', 1)
try:
if 'parameter_scan' in process_name:
import_statement = f'biosimulator_processes.steps.{module_name}'
else:
import_statement = f'biosimulator_processes.processes.{module_name}'

module = __import__(
import_statement, fromlist=[class_name])

# module = importlib.import_module(import_statement)

# Get the class from the module
bigraph_class = getattr(module, class_name)

# Register the process
CORE.process_registry.register(class_name, bigraph_class)
print(f"{class_name} registered successfully.")
except ImportError as e:
print(f"{class_name} not available. Error: {e}")
STEPS_TO_REGISTER = [
('get_sbml', 'get_sbml.GetSbml'),
('plotter', 'viz.CompositionPlotter'),
('plotter2d', 'viz.Plotter2d')]

# core process registry implementation (unique to this package)
CORE = ProcessTypes()

"""
Builder(dataclasses) <- Implementation(dict) <- ProcessBigraph(dict) <- BigraphSchema(dict)
the general builder should make/take dynamically created classes
the biosimulator builder should make/take predefined classes
"""
# core type system implementation (unique to this package)
CORE.type_registry.register('sed_model', schema={'_type': MODEL_TYPE})
register_module(PROCESSES_TO_REGISTER, CORE)
register_module(STEPS_TO_REGISTER, CORE)
20 changes: 20 additions & 0 deletions biosimulator_processes/data_model/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""Data Model Root for Biosimulator Processes
author: Alex Patrie < [email protected] >
license: Apache 2.0
created: 03/2024
"""


from dataclasses import dataclass, asdict, field
from pydantic import BaseModel, ConfigDict


@dataclass
class _BaseClass:
def to_dict(self):
return asdict(self)


class _BaseModel(BaseModel):
model_config = ConfigDict(arbitrary_types_allowed=True)
166 changes: 166 additions & 0 deletions biosimulator_processes/data_model/compare_data_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
from typing import *
from abc import ABC
from dataclasses import dataclass
from biosimulator_processes.utils import prepare_single_ode_process_document

from typing import List, Dict, Tuple, Any
from biosimulator_processes.data_model import _BaseModel as BaseModel


class SimulatorComparisonResult(BaseModel):
simulators: List[str]
value: Dict[Tuple[str], Dict[str, Any]]


class IntervalResult(BaseModel):
global_time_stamp: float
results: Dict[str, Any]


class SimulatorResult(BaseModel):
process_id: str
simulator: str
result: List[IntervalResult]


class ComparisonResults(BaseModel):
duration: int
num_steps: int
values: List[SimulatorResult]


class ProcessAttributes(BaseModel):
name: str
initial_state: Dict[str, Any]
inputs: Dict[str, Any]
outputs: Dict[str, Any]


class CompositeRunError(BaseModel):
exception: Exception


class ComparisonDocument(ABC):
def __init__(self):
pass


class ODEComparisonDocument(ComparisonDocument):
"""To be called 'behind-the-scenes' by the Comparison REST API"""
def __init__(self,
duration: int,
num_steps: int,
model_filepath: str,
framework_type='deterministic',
simulators: Optional[Union[List[str], Dict[str, str]]] = None,
target_parameter: Optional[Dict[str, Union[str, float]]] = None,
**kwargs):
"""This object implements a self generated factory with which it creates its representation. The naming of
simulator processes within the composition are by default generated through concatenating the simulator
tool _name_(i.e: `'tellurium'`) with with a simple index `i` which is a population of an iteration over
the total number of processes in the bigraph.
Args:
simulators:`Union[List[str], Dict[str, str]]`: either a list of actual simulator tool names,
ie: `'copasi'`; or a dict mapping of {simulator_tool_name: custom_process_id}
duration:`int`: the total duration of simulation run
num_steps:`int`
model_filepath:`str`: filepath which points to a SBML model file.
framework_type:`str`: type of mathematical framework to employ with the simulators within your
composition. Choices are `'stochastic'`, `'deterministic'`. Note that there may be more
stochastic options than deterministic.
"""
super().__init__()

if simulators is None:
self.simulators = ['tellurium', 'copasi', 'amici']
elif isinstance(simulators, dict):
self.simulators = list(simulators.keys()) if isinstance(simulators, dict) else simulators
self.custom_process_ids = list(simulators.values())
else:
self.simulators = simulators

self.composite = kwargs.get('composite', {})
self.framework_type = framework_type

context = 'concentrations'
self.species_port_name = f'floating_species_{context}'
self.species_store = [f'floating_species_{context}_store']
self._populate_composition(model_filepath)

def add_single_process_to_composite(self, process_id: str, simulator: str):
process_instance = prepare_single_ode_process_document(
process_id=process_id,
simulator_name=simulator,
sbml_model_fp=self.model_filepath,
add_emitter=False)
self.composite[process_id] = process_instance[process_id]

def _generate_composite_index(self) -> float:
# TODO: implement this.
pass

def _add_emitter(self) -> None: # TODO: How do we reference different nesting levels?
self.composite['emitter'] = {
'_type': 'step',
'address': 'local:ram-emitter',
'config': {
'emit': {
self.species_port_name: 'tree[float]',
'time': 'float'}
},
'inputs': {
self.species_port_name: self.species_store,
'time': ['time_store']}}

def _populate_composition(self, model_filepath: str):
context = 'concentrations'
for index, process in enumerate(self.simulators):
self._add_ode_process_schema(
process_name=process,
species_context=context,
i=index,
model={'model_source': model_filepath})
return self._add_emitter()

def _add_ode_process_schema(
self,
process_name: str,
species_context: str,
i: int,
**config
) -> None:
species_port_name = f'floating_species_{species_context}'
species_store = [f'floating_species_{species_context}_store']
self.composite[f'{process_name}_{i}'] = {
'_type': 'process',
'address': f'local:{process_name}',
'config': config,
'inputs': {
species_port_name: species_store,
'model_parameters': ['model_parameters_store'],
'time': ['time_store'],
'reactions': ['reactions_store']
},
'outputs': {
species_port_name: species_store,
'time': ['time_store']
}
}


class DocumentFactory:
@classmethod
def from_dict(cls, configuration: Dict) -> ComparisonDocument:
"""
Args:
configuration:`Dict`: required keys:
simulators: List[str],
duration: int,
num_steps: int,
model_filepath: str,
framework_type='deterministic',
target_parameter: Dict[str, Union[str, float]] = None
"""
return ComparisonDocument(**configuration)
Loading

0 comments on commit aa28d71

Please sign in to comment.