Skip to content

Commit

Permalink
feat: separated simulation and pre-processing
Browse files Browse the repository at this point in the history
  • Loading branch information
jonrkarr committed Sep 3, 2021
1 parent b8a5f19 commit 7f78eee
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 56 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

FROM python:3.9-slim-buster

ARG VERSION="0.1.12"
ARG VERSION="0.1.13"
ARG SIMULATOR_VERSION=2.6.0

# metadata
Expand Down
2 changes: 1 addition & 1 deletion biosimulators_bionetgen/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.1.12'
__version__ = '0.1.13'
90 changes: 53 additions & 37 deletions biosimulators_bionetgen/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"""

from .io import read_task
from .utils import (exec_bionetgen_task, add_model_attribute_change_to_task, add_simulation_to_task,
from .utils import (exec_bionetgen_task, preprocess_model_attribute_change, add_model_attribute_change_to_task, add_simulation_to_task,
get_variables_results_from_observable_results, add_variables_to_model)
from .warnings import IgnoredBnglFileContentWarning
from biosimulators_utils.combine.exec import exec_sedml_docs_in_archive
Expand Down Expand Up @@ -100,7 +100,7 @@ def exec_sed_task(task, variables, preprocessed_task=None, log=None, config=None
Args:
task (:obj:`Task`): SED task
variables (:obj:`list` of :obj:`Variable`): variables that should be recorded
preprocessed_task (:obj:`object`, optional): preprocessed information about the task, including possible
preprocessed_task (:obj:`dict`, optional): preprocessed information about the task, including possible
model changes and variables. This can be used to avoid repeatedly executing the same initialization
for repeated calls to this method.
log (:obj:`TaskLog`, optional): log for the task
Expand Down Expand Up @@ -136,12 +136,56 @@ def exec_sed_task(task, variables, preprocessed_task=None, log=None, config=None
* :obj:`get_variables_results_from_observable_results`
"""
config = config or get_config()

if config.LOG and not log:
log = TaskLog()

if preprocessed_task is None:
preprocessed_task = preprocess_sed_task(task, variables, config=config)

# read the model from the BNGL file
bionetgen_task = preprocessed_task['bionetgen_task']

# validate and apply the model attribute changes to the BioNetGen task
for change in task.model.changes:
add_model_attribute_change_to_task(bionetgen_task, change, preprocessed_task['model_changes'][change.target])

# apply the SED algorithm and its parameters to the BioNetGen task
alg_kisao_id = preprocessed_task['algorithm_kisao_id']

# execute the task
observable_results = exec_bionetgen_task(bionetgen_task)

# get predicted values of the variables
variable_results = get_variables_results_from_observable_results(observable_results, variables)
for key in variable_results.keys():
variable_results[key] = variable_results[key][-(task.simulation.number_of_points + 1):]

# log action
if config.LOG:
log.algorithm = alg_kisao_id
log.simulator_details = {
'actions': bionetgen_task.actions,
}

# return the values of the variables and log
return variable_results, log


def preprocess_sed_task(task, variables, config=None):
""" Preprocess a SED task, including its possible model changes and variables. This is useful for avoiding
repeatedly initializing tasks on repeated calls of :obj:`exec_sed_task`.
Args:
task (:obj:`Task`): task
variables (:obj:`list` of :obj:`Variable`): variables that should be recorded
config (:obj:`Config`, optional): BioSimulators common configuration
Returns:
:obj:`dict`: preprocessed information about the task
"""
config = config or get_config()

if config.VALIDATE_SEDML:
raise_errors_warnings(
validation.validate_task(task),
Expand Down Expand Up @@ -174,47 +218,19 @@ def exec_sed_task(task, variables, preprocessed_task=None, log=None, config=None
bionetgen_task.actions = []

# validate and apply the model attribute changes to the BioNetGen task
model_changes = {}
for change in task.model.changes:
add_model_attribute_change_to_task(bionetgen_task, change)
model_changes[change.target] = preprocess_model_attribute_change(bionetgen_task, change)

# add observables for the variables to the BioNetGen model
add_variables_to_model(bionetgen_task.model, variables)

# apply the SED algorithm and its parameters to the BioNetGen task
alg_kisao_id = add_simulation_to_task(bionetgen_task, task.simulation)

# execute the task
observable_results = exec_bionetgen_task(bionetgen_task)

# get predicted values of the variables
variable_results = get_variables_results_from_observable_results(observable_results, variables)
for key in variable_results.keys():
variable_results[key] = variable_results[key][-(task.simulation.number_of_points + 1):]

# log action
if config.LOG:
log.algorithm = alg_kisao_id
log.simulator_details = {
'actions': bionetgen_task.actions,
}

# return the values of the variables and log
return variable_results, log


def preprocess_sed_task(task, variables, config=None):
""" Preprocess a SED task, including its possible model changes and variables. This is useful for avoiding
repeatedly initializing tasks on repeated calls of :obj:`exec_sed_task`.
Args:
task (:obj:`Task`): task
variables (:obj:`list` of :obj:`Variable`): variables that should be recorded
preprocessed_task (:obj:`PreprocessedTask`, optional): preprocessed information about the task, including possible
model changes and variables. This can be used to avoid repeatedly executing the same initialization for repeated
calls to this method.
config (:obj:`Config`, optional): BioSimulators common configuration
Returns:
:obj:`object`: preprocessed information about the task
"""
pass
return {
'bionetgen_task': bionetgen_task,
'model_changes': model_changes,
'algorithm_kisao_id': alg_kisao_id,
}
77 changes: 60 additions & 17 deletions biosimulators_bionetgen/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import tempfile

__all__ = [
'preprocess_model_attribute_change',
'add_model_attribute_change_to_task',
'add_variables_to_model',
'add_simulation_to_task',
Expand All @@ -34,8 +35,8 @@
]


def add_model_attribute_change_to_task(task, change):
""" Encode SED model attribute changes into a BioNetGen task
def preprocess_model_attribute_change(task, change):
""" Process a model change
* Compartment sizes: targets should follow the pattern ``compartments.<compartment_id>.size``
* Function expressions: targets should follow the pattern ``functions.<function_id>.expression``
Expand All @@ -46,11 +47,13 @@ def add_model_attribute_change_to_task(task, change):
task (:obj:`Task`): BioNetGen task
change (:obj:`ModelAttributeChange`): model attribute change
Returns:
:obj:`dict`: processed information about the model change
Raises:
:obj:`ValueError`: if a target of a change is not valid
"""
target = change.target
new_value = change.new_value

compartment_size_match = re.match(r'^compartments\.([^\.]+)(\.size)?$', target)
if compartment_size_match:
Expand All @@ -62,25 +65,32 @@ def add_model_attribute_change_to_task(task, change):
for i_line, line in enumerate(block):
match = re.match(pattern, line)
if match:
block[i_line] = '{} {} {} {}'.format(obj_id, match.group(1), new_value, (match.group(3) or '').strip()).strip()
comp_changed = True
return {
'type': 'replace_line_in_block',
'block': block,
'i_line': i_line,
'new_line': lambda new_value: '{} {} {} {}'.format(obj_id, match.group(1), new_value, (match.group(3) or '').strip()).strip(),
}

if not comp_changed:
raise ValueError(('The size of compartment `{}` cannot be changed '
'because the model does not have a compartment with this id.').format(obj_id))

return

parameter_values_match = re.match(r'^parameters\.([^\.]+)(\.value)?$', target)
if parameter_values_match:
task.actions.append('setParameter("{}", {})'.format(parameter_values_match.group(1), new_value))
return
return {
'type': 'append_action',
'action': lambda new_value: 'setParameter("{}", {})'.format(parameter_values_match.group(1), new_value),
}

species_counts_match = re.match(r'^species\.([^\.]+)\((.*?)\)(\.initialCount)?$', target)
if species_counts_match:
task.actions.append('setConcentration("{}({})", {})'.format(
species_counts_match.group(1), species_counts_match.group(2), new_value))
return
return {
'type': 'append_action',
'action': lambda new_value: 'setConcentration("{}({})", {})'.format(
species_counts_match.group(1), species_counts_match.group(2), new_value),
}

functions_expression_match = re.match(r'^functions\.([^\.\(\)]+)(\.expression)?$', target)
if functions_expression_match:
Expand All @@ -94,14 +104,17 @@ def add_model_attribute_change_to_task(task, change):
match = re.match(pattern, line)
if match:
func_changed = True
block[i_line] = '{}({}) = {}'.format(obj_id, match.group(1), new_value)
return {
'type': 'replace_line_in_block',
'block': block,
'i_line': i_line,
'new_line': lambda new_value: '{}({}) = {}'.format(obj_id, match.group(1), new_value),
}

if not func_changed:
raise ValueError(('The expression of function `{}` cannot be changed '
'because the model does not have a function with this id.').format(obj_id))

return

function_args_expression_match = re.match(r'^functions\.([^\.]+)\((.*?)\)(\.expression)?$', target)
if function_args_expression_match:
obj_id = function_args_expression_match.group(1)
Expand All @@ -115,14 +128,17 @@ def add_model_attribute_change_to_task(task, change):
match = re.match(pattern, line)
if match:
func_changed = True
block[i_line] = '{}({}) = {}'.format(obj_id, obj_args, new_value)
return {
'type': 'replace_line_in_block',
'block': block,
'i_line': i_line,
'new_line': lambda new_value: '{}({}) = {}'.format(obj_id, obj_args, new_value),
}

if not func_changed:
raise ValueError(('The expression of function `{}` cannot be changed '
'because the model does not have a function with this id.').format(obj_id))

return

target_patterns = {
'compartment size': compartment_size_match,
'parameter value': parameter_values_match,
Expand All @@ -135,6 +151,33 @@ def add_model_attribute_change_to_task(task, change):
raise NotImplementedError(msg)


def add_model_attribute_change_to_task(task, change, preprocessed_change=None):
""" Encode SED model attribute changes into a BioNetGen task
* Compartment sizes: targets should follow the pattern ``compartments.<compartment_id>.size``
* Function expressions: targets should follow the pattern ``functions.<function_id>.expression``
* Initial species counts: targets should follow the pattern ``species.<species_id>.count``
* Parameter values: targets should follow the pattern ``parameters.<parameter_id>.value``
Args:
task (:obj:`Task`): BioNetGen task
change (:obj:`ModelAttributeChange`): model attribute change
preprocessed_change (:obj:`dict`): preprocessed information about the change
Raises:
:obj:`ValueError`: if a target of a change is not valid
"""
if preprocessed_change is None:
preprocessed_change = preprocess_model_attribute_change(task, change)

new_value = change.new_value

if preprocessed_change['type'] == 'replace_line_in_block':
preprocessed_change['block'][preprocessed_change['i_line']] = preprocessed_change['new_line'](new_value)
else:
task.actions.append(preprocessed_change['action'](new_value))


def add_variables_to_model(model, variables):
""" Encode SED variables into observables in a BioNetGen task
Expand Down

0 comments on commit 7f78eee

Please sign in to comment.