Source code for bionetgen.network.blocks

try:
    from typing import OrderedDict
except ImportError:
    from collections import OrderedDict
from .structs import NetworkParameter, NetworkCompartment, NetworkGroup
from .structs import NetworkSpecies, NetworkFunction, NetworkReaction
from .structs import NetworkEnergyPattern, NetworkPopulationMap

# this import fails on some python versions
try:
    from typing import OrderedDict
except ImportError:
    from collections import OrderedDict


###### BLOCK OBJECTS ######
[docs]class NetworkBlock: """ Base block object that will be used for each block in the network. Attributes ---------- name : str Name of the block which will be used to write the BNGL text comment : (str, str) comment at the begin {block} or end {block} statements, tuple items : OrderedDict all the model objects in the block Methods ------- add_item((name,value)) sets self.item[name] = value to add a particular model object into a block add_items(item_list) loops over every element in the list and uses add_item on it gen_string() for every block this method generates the BNGL string of the block. it has to be overwritten for each block. """ def __init__(self) -> None: self.name = "NetworkBlock" self.comment = (None, None) self.items = OrderedDict() def __str__(self) -> str: return self.gen_string() def __len__(self) -> int: return len(self.items) def __repr__(self) -> str: # overwrites what the class representation # shows the items in the model block in # say ipython repr_str = "{} block with {} item(s): {}".format( self.name, len(self.items), list([i.name for i in self.items.values()]) ) return repr_str def __getitem__(self, key): if isinstance(key, int): # get the item in order return list(self.items.keys())[key] return self.items[key] def __setitem__(self, key, value) -> None: self.items[key] = value def __delitem__(self, key) -> None: if key in self.items: self.items.pop(key) else: print("Item {} not found".format(key)) def __iter__(self): return self.items.keys().__iter__() def __contains__(self, key) -> bool: return key in self.items # TODO: Think extensively how this is going to work def __setattr__(self, name, value) -> None: changed = False if hasattr(self, "items"): if name in self.items.keys(): try: new_value = float(value) changed = True self.items[name] = new_value except: self.items[name] = value if changed: self._changes[name] = new_value self.__dict__[name] = new_value else: self.__dict__[name] = value
[docs] def gen_string(self) -> str: # each block can have a comment at the start if self.comment[0] is not None: block_lines = ["\nbegin {} #{}".format(self.name, self.comment[0])] else: block_lines = ["\nbegin {}".format(self.name)] # now we just loop over lines for item in self.items.keys(): block_lines.append(self.items[item].print_line()) # each block can have a comment at the start if self.comment[1] is not None: block_lines.append("end {} #{}\n".format(self.name, self.comment[1])) else: block_lines.append("end {}\n".format(self.name)) # join everything with new lines return "\n".join(block_lines)
[docs] def add_item(self, item_tpl) -> None: # TODO: try adding evaluation of the parameter here # for the future, in case we want people to be able # to adjust the math # TODO: Error handling, some names will definitely break this name, value = item_tpl # allow for empty addition, uses index if name is None: name = len(self.items) # set the line self.items[name] = value # if the name is a string, try adding as an attribute if isinstance(name, str): try: setattr(self, name, value) except: # print("can't set {} to {}".format(name, value)) pass # we just added an item to a block, let's assume we need # to recompile if we have a compiled simulator self._recompile = True
[docs] def add_items(self, item_list) -> None: for item in item_list: self.add_item(item)
[docs]class NetworkParameterBlock(NetworkBlock): """ Parameter block object, subclass of ModelBlock. Methods ------- add_parameter(name, value, expr=None) adds a parameter by making a new Parameter object and passing the args/kwargs to its initialization. """ def __init__(self) -> None: super().__init__() self.name = "parameters" def __setattr__(self, name, value) -> None: changed = False if hasattr(self, "items"): if name in self.items: if isinstance(value, NetworkParameter): # New parameter object changed = True self.items[name] = value elif isinstance(value, str): # A new expression if self.items[name]["value"] != value: changed = True self.items[name]["value"] = value self.items[name].write_expr = True else: try: # try a new value, we need to make sure # to stop printing out the expression value = float(value) if self.items[name]["value"] != value: changed = True self.items[name]["value"] = value self.items[name].write_expr = False except: print( "can't set parameter {} to {}".format( self.items[name]["name"], value ) ) if changed: self._changes[name] = value self.__dict__[name] = value else: self.__dict__[name] = value else: self.__dict__[name] = value
[docs] def add_parameter(self, *args, **kwargs) -> None: p = NetworkParameter(*args, **kwargs) self.add_item((p.name, p))
[docs]class NetworkCompartmentBlock(NetworkBlock): """ Compartment block object, subclass of ModelBlock. Methods ------- add_compartment(name, dim, size, outside=None) adds a compartment by making a new Compartment object and passing the args/kwargs to its initialization. """ def __init__(self) -> None: super().__init__() self.name = "compartments" def __setattr__(self, name, value) -> None: changed = False if hasattr(self, "items"): if name in self.items: if isinstance(value, NetworkCompartment): changed = True self.items[name] = value elif isinstance(value, str): if self.items[name]["name"] != value: changed = True self.items[name]["name"] = value else: try: value = float(value) if self.items[name]["size"] != value: changed = True self.items[name]["size"] = value except: print( "can't set compartment {} to {}".format( self.items[name]["name"], value ) ) if changed: self._changes[name] = value self.__dict__[name] = value else: self.__dict__[name] = value else: self.__dict__[name] = value
[docs] def add_compartment(self, *args, **kwargs) -> None: c = NetworkCompartment(*args, **kwargs) self.add_item((c.name, c))
[docs]class NetworkGroupBlock(NetworkBlock): """ Group block object, subclass of NetworkBlock. Methods ------- add_group(name, otype, patterns=[]) adds an group by making a new NetworkGroup object and passing the args/kwargs to its initialization. """ def __init__(self) -> None: super().__init__() self.name = "groups" def __setattr__(self, name, value) -> None: changed = False if hasattr(self, "items"): if name in self.items: if isinstance(value, NetworkGroup): changed = True self.items[name] = value elif isinstance(value, str): if self.items[name]["name"] != value: changed = True self.items[name]["name"] = value else: print( "can't set group {} to {}".format( self.items[name]["name"], value ) ) if changed: self._changes[name] = value self.__dict__[name] = value else: self.__dict__[name] = value else: self.__dict__[name] = value
[docs] def add_group(self, *args, **kwargs) -> None: g = NetworkGroup(*args, **kwargs) self.add_item((g.name, g))
[docs]class NetworkSpeciesBlock(NetworkBlock): """ Species block object, subclass of NetworkBlock. Methods ------- add_species(name, pattern=Pattern(), count=0) adds a species by making a new Species object and passing the args/kwargs to its initialization. """ def __init__(self) -> None: super().__init__() self.name = "species" def __setattr__(self, name, value) -> None: changed = False if hasattr(self, "items"): if name in self.items: if isinstance(value, NetworkSpecies): changed = True self.items[name] = value elif isinstance(value, str): if self.items[name]["name"] != value: changed = True self.items[name]["name"] = value else: print( "can't set species {} to {}".format( self.items[name]["name"], value ) ) if changed: self._changes[name] = value self.__dict__[name] = value else: self.__dict__[name] = value else: self.__dict__[name] = value def __getitem__(self, key): return self.items[key] def __setitem__(self, key, value) -> None: self.items[key] = value
[docs] def add_species(self, *args, **kwargs) -> None: s = NetworkSpecies(*args, **kwargs) ctr = len(self.items) self.add_item((ctr, s))
[docs]class NetworkFunctionBlock(NetworkBlock): """ Function block object, subclass of NetworkBlock. Methods ------- add_function(name, name, expr, args=None) adds a function by making a new Function object and passing the args/kwargs to its initialization. """ def __init__(self) -> None: super().__init__() self.name = "functions" def __setattr__(self, name, value) -> None: changed = False if hasattr(self, "items"): if name in self.items: if isinstance(value, NetworkFunction): changed = True self.items[name] = value elif isinstance(value, str): if self.items[name]["expr"] != value: changed = True self.items[name]["expr"] = value else: print( "can't set function {} to {}".format( self.items[name]["name"], value ) ) if changed: self._changes[name] = value self.__dict__[name] = value else: self.__dict__[name] = value else: self.__dict__[name] = value
[docs] def add_function(self, *args, **kwargs) -> None: f = NetworkFunction(*args, **kwargs) self.add_item((f.name, f))
[docs]class NetworkReactionBlock(NetworkBlock): """ Rule block object, subclass of NetworkBlock. Methods ------- add_rule(name, name, reactants=[], products=[], rate_constants=()) adds a rule by making a new Rule object and passing the args/kwargs to its initialization. consolidate_rules : None XML loading makes it so that reversible rules are split into two unidirectional rules. This find them and combines them into a single rule to correctly represent the original model rule. """ def __init__(self) -> None: super().__init__() self.name = "reactions" def __setattr__(self, name, value) -> None: changed = False if hasattr(self, "items"): if name in self.items: if isinstance(value, NetworkReaction): changed = True self.items[name] = value elif isinstance(value, str): if self.items[name]["name"] != value: changed = True self.items[name]["name"] = value else: print( "can't set rule {} to {}".format( self.items[name]["name"], value ) ) if changed: self._changes[name] = value self.__dict__[name] = value else: self.__dict__[name] = value else: self.__dict__[name] = value
[docs] def add_reaction(self, *args, **kwargs) -> None: r = NetworkReaction(*args, **kwargs) self.add_item((r.name, r))
[docs]class NetworkEnergyPatternBlock(NetworkBlock): """ Energy pattern block object, subclass of NetworkBlock. Methods ------- add_energy_pattern(id, pattern, expression) adds an energy pattern by making a new EnergyPattern object and passing the args/kwargs to its initialization. """ def __init__(self) -> None: super().__init__() self.name = "energy patterns" def __setattr__(self, name, value) -> None: changed = False if hasattr(self, "items"): if name in self.items: if isinstance(value, NetworkEnergyPattern): changed = True self.items[name] = value elif isinstance(value, str): if self.items[name]["name"] != value: changed = True self.items[name]["name"] = value else: print( "can't set energy pattern {} to {}".format( self.items[name]["name"], value ) ) if changed: self._changes[name] = value self.__dict__[name] = value else: self.__dict__[name] = value else: self.__dict__[name] = value
[docs] def add_energy_pattern(self, *args, **kwargs) -> None: ep = NetworkEnergyPattern(*args, **kwargs) self.add_item((ep.name, ep))
[docs]class NetworkPopulationMapBlock(NetworkBlock): """ Population map block object, subclass of NetworkBlock. Methods ------- add_population_map(id, struct_species, pop_species, rate) adds a population map by making a new PopulationMap object and passing the args/kwargs to its initialization """ def __init__(self) -> None: super().__init__() self.name = "population maps" def __setattr__(self, name, value) -> None: changed = False if hasattr(self, "items"): if name in self.items: if isinstance(value, NetworkPopulationMap): changed = True self.items[name] = value elif isinstance(value, str): if self.items[name]["name"] != value: changed = True self.items[name]["name"] = value else: print( "can't set population map {} to {}".format( self.items[name]["name"], value ) ) if changed: self._changes[name] = value self.__dict__[name] = value else: self.__dict__[name] = value else: self.__dict__[name] = value
[docs] def add_population_map(self, *args, **kwargs) -> None: pm = NetworkPopulationMap(*args, **kwargs) self.add_item((pm.name, pm))