Source code for bionetgen.modelapi.structs

from bionetgen.modelapi.pattern import Molecule, Pattern
from bionetgen.modelapi.rulemod import RuleMod
from bionetgen.core.utils.utils import ActionList
from bionetgen.core.exc import BNGParseError


[docs]class ModelObj: """ The base class for all items in a model (parameter, observable etc.). Attributes ---------- comment : str comment at the end of the line/object line_label : str line label at the beginning of the line/object Methods ------- print_line() generates the actual line string with line label and comments if applicable gen_string() generates the BNGL string of the object itself, separate from line attributes """ def __init__(self): self._comment = None self._line_label = None def __str__(self) -> str: return self.gen_string() def __repr__(self) -> str: return self.gen_string() def __contains__(self, key): return hasattr(self, key) def __getitem__(self, key): return getattr(self, key) def __setitem__(self, key, value): setattr(self, key, value) def __delitem__(self, key): delattr(self, key) @property def comment(self) -> None: return self._comment @comment.setter def comment(self, val) -> None: # TODO: regex handling of # instead if val.startswith("#"): self._comment = val[1:] else: self._comment = val @property def line_label(self) -> str: return self._line_label @line_label.setter def line_label(self, val) -> None: # TODO: specific error handling try: ll = int(val) self._line_label = "{} ".format(ll) except: self._line_label = "{}: ".format(val)
[docs] def print_line(self) -> str: s = " " # let's deal with line label if self.line_label is not None: s += self.line_label # start building the rest of the string s += str(self) if self.comment is not None: s += " #{}".format(self.comment) return s
[docs]class Parameter(ModelObj): """ Class for all parameters in the model, subclass of ModelObj. In BNGL parameters are of the form parameter_name parameter_value/expression or parameter_name = parameter_value/expression Attributes ---------- name : str name of the parameter value : str value of the parameter, if loaded from XML this will always exist since NFsim needs the value and not the expression expr : str this exists if the parameter is a math expression, not necerssary write_expr : bool this is a boolean that determines if the generated string has is in expression form or in value form. """ def __init__(self, name, value, expr=None): super().__init__() self.name = name self.value = value self.expr = expr try: test = float(expr) self.write_expr = False except: self.write_expr = True
[docs] def gen_string(self) -> str: if self.write_expr: return "{} {}".format(self.name, self.expr) else: return "{} {}".format(self.name, self.value)
[docs]class Compartment(ModelObj): """ Class for all compartments in the model, subclass of ModelObj. In BNGL the compartments are of the form compartment_name dimensions size or compartment_name dimensions size parent_compartment the second form only applies when one compartment is contained in another compartment. Attributes ---------- name : str name of the compartment dim : str dimensionality of the compartment size : str size/volume of the compartment outside : str parent compartment, if exists write_expr : bool boolean that describes if the size is a volume or an expression """ def __init__(self, name, dim, size, outside=None): super().__init__() self.name = name self.dim = dim self.size = size try: test = float(size) self.write_expr = False except: self.write_expr = True self.outside = outside
[docs] def gen_string(self) -> str: s = "{} {} {}".format(self.name, self.dim, self.size) if self.outside is not None: s += " {}".format(self.outside) return s
[docs]class Observable(ModelObj): """ Class for all observables in the model, subclass of ModelObj. In BNGL the observables are of the form observable_type observable_name observable_patterns where patterns can include multiple patterns separated by commas. Attributes ---------- name : str name of the observable type : str type of the observable, Molecules or Species patterns : list[Pattern] list of patterns of the observable Methods ------- add_pattern add a Pattern object into the list of patterns for this observable """ def __init__(self, name, otype, patterns=[]): super().__init__() self.name = name self.type = otype if self.type == "Species": for pat in patterns: if pat.MatchOnce: pat.MatchOnce = False self.patterns = patterns
[docs] def gen_string(self) -> str: s = "{} {} ".format(self.type, self.name) for ipat, pat in enumerate(self.patterns): if ipat > 0: s += "," s += str(pat) return s
[docs] def add_pattern(self, pat) -> None: # if type is species, set MatchOnce to false since all species automatically match once if self.type == "Species": pat.MatchOnce = False self.patterns.append(pat)
[docs]class MoleculeType(ModelObj): """ Class for all molecule types in the model, subclass of ModelObj. In BNGL the molecule types are of the form molecule_type where all possible states of each component of a molecule is listed, e.g. A(b, p~0~1, k~ON~OFF~NULL) Attributes ---------- molecule : Molecule a molecule type only contains a molecule object which can also handle multiple component states """ def __init__(self, name, components): super().__init__() self.name = name self.molecule = Molecule(name=name, components=components)
[docs] def gen_string(self) -> str: return str(self.molecule)
[docs]class Species(ModelObj): """ Class for all species in the model, subclass of ModelObj. In BNGL the species/seed species are of the form species count where species is a single pattern and count is the starting value for that specific pattern Attributes ---------- pattern : Pattern pattern of the seed species count : str starting value of the seed species """ def __init__(self, pattern=Pattern(), count=0): super().__init__() self.pattern = pattern self.count = count self.name = str(self.pattern)
[docs] def gen_string(self) -> str: s = "{} {}".format(self.pattern, self.count) return s
[docs]class Function(ModelObj): """ Class for all functions in the model, subclass of ModelObj. In BNGL functions are of the form function_name function_expression or function_name = function_expression and functions can have arguments function_name(arg1, arg2, ..., argN) Attributes ---------- name : str name of the function expr : str function expression args : list optional list of arguments for the function """ def __init__(self, name, expr, args=None): super().__init__() self.name = name self.expr = expr self.args = args
[docs] def gen_string(self) -> str: if self.args is None: s = "{} = {}".format(self.name, self.expr) else: s = "{}({}) = {}".format(self.name, ",".join(self.args), self.expr) return s
[docs]class Action(ModelObj): """ Class for all actions in the model, subclass of ModelObj. In BNGL actions are of the form action_type({arg1=>value1, arg2=>value2, ...}) Attributes ---------- type : str type of action, e.g. simulate or writeFile args : dict[arg_name] = arg_value action arguments as keys and their values as values """ def __init__(self, action_type=None, action_args={}) -> None: super().__init__() AList = ActionList() self.normal_types = AList.normal_types self.no_setter_syntax = AList.no_setter_syntax self.square_braces = AList.square_braces self.possible_types = AList.possible_types # Set initial values self.name = action_type self.type = action_type self.args = action_args # check type if self.type not in self.possible_types: raise BNGParseError(message=f"Action type {self.type} not recognized!") seen_args = [] for arg in action_args: arg_name, arg_value = arg, action_args[arg] valid_arg_list = AList.arg_dict[self.type] # TODO: actions that don't take argument names should be parsed separately to check validity of arg-val tuples # TODO: currently not type checking arguments if valid_arg_list is None: raise BNGParseError( message=f"Argument {arg_name} is given, but action {self.type} does not take arguments" ) if len(valid_arg_list) > 0: if arg_name not in AList.arg_dict[self.type]: raise BNGParseError( message=f"Action argument {arg_name} not recognized!\nCheck to make sure action is correctly formatted" ) # TODO: If arg_value is the correct type if arg_name in seen_args: print( f"Warning: argument {arg_name} already given, using latter value {arg_value}" ) else: seen_args.append(arg_name)
[docs] def gen_string(self) -> str: # TODO: figure out every argument that has special # requirements, e.g. method requires the value to # be a string action_str = "{}(".format(self.type) # we can skip curly if we don't have arguments # and we NEED to skip it for some actions if self.type in self.normal_types and not len(self.args) == 0: action_str += "{" elif self.type in self.square_braces: action_str += "[" # add arguments for iarg, arg in enumerate(self.args): val = self.args[arg] if iarg > 0: action_str += "," # some actions need =>, some don't if self.type in self.normal_types: action_str += f"{arg}=>{val}" else: action_str += f"{arg}" # we can skip curly if we don't have arguments # and we NEED to skip it for some actions if self.type in self.normal_types and not len(self.args) == 0: action_str += "}" elif self.type in self.square_braces: action_str += "]" # close up the action action_str += ")" return action_str
[docs] def print_line(self) -> str: s = "" # let's deal with line label if self.line_label is not None: s += self.line_label # start building the rest of the string s += str(self) if self.comment is not None: s += " #{}".format(self.comment) return s
[docs]class Rule(ModelObj): """ Class for all rules in the model, subclass of ModelObj. Attributes ---------- name : str name of the rule, optional reactants : list[Pattern] list of patterns for reactants products : list[Pattern] list of patterns for products rule_mod : RuleMod modifier (moveConnected, TotalRate, etc.) used by a given rule operations : list[Operation] list of operations Methods ------- set_rate_constants((k_fwd,k_bck)) sets forward and backwards rate constants, backwards rate constants are optional and if not given, will set the rule to be a unidirectional rule side_string(list[Pattern]) given a list of patterns, return a string formatted to be on one side of a rule definition """ def __init__( self, name, reactants=[], products=[], rate_constants=(), rule_mod=RuleMod(), operations=[], ) -> None: super().__init__() self.name = name self.reactants = reactants self.products = products if rule_mod is None: self.rule_mod = RuleMod() else: self.rule_mod = rule_mod self.operations = operations self.set_rate_constants(rate_constants)
[docs] def set_rate_constants(self, rate_cts): if len(rate_cts) == 1: self.rate_constants = [rate_cts[0]] self.bidirectional = False elif len(rate_cts) == 2: self.rate_constants = [rate_cts[0], rate_cts[1]] self.bidirectional = True else: print("1 or 2 rate constants allowed")
[docs] def gen_string(self): if self.bidirectional: return "{}: {} <-> {} {},{} {}".format( self.name, self.side_string(self.reactants), self.side_string(self.products), self.rate_constants[0], self.rate_constants[1], str(self.rule_mod), ) else: return "{}: {} -> {} {} {}".format( self.name, self.side_string(self.reactants), self.side_string(self.products), self.rate_constants[0], str(self.rule_mod), )
[docs] def side_string(self, patterns): side_str = "" for ipat, pat in enumerate(patterns): if ipat > 0: side_str += " + " side_str += str(pat) return side_str
[docs]class EnergyPattern(ModelObj): """ Class for all energy patterns in the model, subclass of ModelObj. In BNGL the energy patterns are of the form EP_pattern EP_expression Attributes ---------- name : str id of the energy pattern pattern : Pattern Pattern object representing the energy pattern expression : str expression used for energy pattern """ def __init__(self, name, pattern, expression): super().__init__() self.name = name self.pattern = pattern self.expression = expression
[docs] def gen_string(self) -> str: s = "{} {}".format(self.pattern, self.expression) return s
[docs]class PopulationMap(ModelObj): """ Class for all population maps in the model, subclass of ModelObj. In BNGL the population maps are of the form structured_species -> population_species lumping_parameter Attributes ---------- name : str id of the population map struct_species : Pattern Pattern object representing the species to be mapped pop_species : Pattern Pattern object representing the population count rate : str lumping parameter used in population mapping """ def __init__(self, name, struct_species, pop_species, rate): super().__init__() self.name = name self.species = struct_species self.population = pop_species self.rate = rate
[docs] def gen_string(self) -> str: s = "{} -> {} {}".format(self.species, self.population, self.rate) return s