"""Function to import maboss models in Python.
Multiple types are supported:
- MaBoSS format (bnd + cfg files)
- SBML format (xml files)
- BNet format (bnet files)
- TabularQual format (xlsx files)
"""
from __future__ import print_function
import sys
from collections import OrderedDict
from sys import stderr, version_info
import tempfile
if version_info[0] < 3:
from contextlib2 import ExitStack
else:
from contextlib import ExitStack
from os.path import isfile
import pyparsing as pp
from .logic import varName
from .network import Node, Network
from .simulation import Simulation, sbml_to_maboss, bnet_to_maboss
from .cmabosssimulation import CMaBoSSSimulation
from .sbmlsimulation import SBMLSSimulation
from .bnetsimulation import BNetSimulation
externVar = pp.Suppress('$') + ~pp.White() + varName
externVar.setParseAction(lambda token: token[0])
import uuid
import re
# ====================
# bnd grammar
# ====================
internal_var_decl = pp.Group(varName('lhs') + pp.Suppress('=')
+ pp.SkipTo(';')('rhs') + pp.Suppress(';'))
node_decl = pp.Group(pp.Suppress(pp.CaselessKeyword("Node")) + varName("name") + pp.Suppress('{')
+ pp.OneOrMore(internal_var_decl)('interns')
+ pp.Suppress('}'))
bnd_grammar = pp.OneOrMore(node_decl)
bnd_grammar.ignore('//' + pp.restOfLine)
# =====================
# cfg grammar
# =====================
intPart = pp.Word(pp.nums)
intPart.setParseAction(lambda token: int(token[0]))
floatNum = pp.Word(pp.nums + '.' + 'E' + 'e' + '-' + '+')
floatNum.setParseAction(lambda token: float(token[0]))
booleanStr = (pp.oneOf('0 1')
| pp.CaselessLiteral("True") | pp.CaselessLiteral("False"))
def booleanStrAction(token):
if pp.CaselessLiteral("True").matches(token) or pp.Word("1").matches(token):
return True
elif pp.CaselessLiteral("False").matches(token) or pp.Word("0").matches(token):
return False
else:
print("Cannot recognize boolean value : %s" % token)
return token
booleanStr.setParseAction(lambda token: booleanStrAction(token[0]))
numOrBool = (floatNum | booleanStr)("value")
var_decl = pp.Group(externVar("lhs") + pp.Suppress('=')
+ pp.SkipTo(';')("rhs") + pp.Suppress(';'))
param_decl = pp.Group(varName("param") + pp.Suppress('=')
+ numOrBool + pp.Suppress(';'))
stateSet = (pp.Suppress('[') + pp.Group(pp.delimitedList(intPart))
+ pp.Suppress(']'))
stateSet.setParseAction(lambda token: list(token))
stateProb = pp.Word(pp.alphanums+' ()+-*/$._')('proba') + stateSet("states")
stateProb.setParseAction(lambda token: (token.proba, token.states))
schedule = pp.Group(pp.delimitedList(floatNum + pp.Suppress(':') + (floatNum | booleanStr)))
istate_decl = pp.Group(pp.Suppress('[') + pp.delimitedList(varName)("nodes")
+ pp.Suppress('].istate') + pp.Suppress('=')
+ pp.delimitedList(stateProb)('attrib') + pp.Suppress(';'))
oneIstate_decl = pp.Group(varName("nd_i") + ~pp.White() + pp.Suppress('.istate')
+ pp.Suppress('=') + booleanStr('istate_val')
+ pp.Suppress(';'))
internal_decl = pp.Group(varName("internal") + ~pp.White()
+ pp.Suppress(".is_internal") + pp.Suppress('=')
+ booleanStr("is_internal_val")
+ pp.Suppress(';'))
ingraph_decl = pp.Group(varName("ingraph") + ~pp.White()
+ pp.Suppress(".is_graph") + pp.Suppress('=')
+ booleanStr("is_graph_val")
+ pp.Suppress(';'))
refstate_decl = pp.Group(varName("refstate") + ~pp.White()
+ pp.Suppress(".refstate") + pp.Suppress('=')
+ numOrBool("refstate_val")
+ pp.Suppress(';'))
schedule_decl = pp.Group(varName("schedule") + ~pp.White()
+ pp.Suppress(".schedule") + pp.Suppress('=')
+ schedule("schedule_val")
+ pp.Suppress(';'))
cfg_decl = (var_decl | istate_decl | param_decl | internal_decl
| oneIstate_decl | refstate_decl | schedule_decl)
cfg_grammar = pp.ZeroOrMore(cfg_decl)
cfg_grammar.ignore('//' + pp.restOfLine)
[docs]
def loadTabularQual(tabularqual_filename, cmaboss=False):
"""Loads a network from a TabularQual format file.
:param str tabularqual_filename: Network file
:rtype: :py:class:`.Simulation`
"""
if "://" in tabularqual_filename:
from colomoto_jupyter.io import ensure_localfile
tabularqual_filename = ensure_localfile(tabularqual_filename)
with tempfile.NamedTemporaryFile(suffix=".sbml", delete=True) as sbml_file:
sbml_file.close() # we only need the name of the file, it will be overwritten by the convert_spreadsheet_to_sbml function, but on Windows the file cannot be opened twice at the same time
try:
from tabularqual.convert_spreadsheet_to_sbml import convert_spreadsheet_to_sbml
convert_spreadsheet_to_sbml(
tabularqual_filename,
sbml_file.name,
interactions_anno=False, # no existing interaction annotations
transitions_anno=True,
validate=False, # no validation
print_messages=False, # no messages,
use_name=False
)
return loadSBML(sbml_file.name, cmaboss=cmaboss)
except ImportError:
raise ImportError("TabularQual is not installed. Install it with 'pip install tabularqual' to load TabularQual files.")
[docs]
def loadBNet(bnet_filename, cfg_filename=None, cmaboss=False):
"""Loads a network from a MaBoSS format file.
:param str bnet_filename: Network file
:param str cfg_filename: Configuration file
:rtype: :py:class:`.Simulation`
"""
assert bnet_filename.lower().endswith(".bnet"), "wrong extension for BNet file"
if "://" in bnet_filename:
from colomoto_jupyter.io import ensure_localfile
bnet_filename = ensure_localfile(bnet_filename)
if cmaboss:
return BNetSimulation(bnet_filename, cfg_filename)
return bnet_to_maboss(bnet_filename, cfg_filename)
[docs]
def loadSBML(sbml_filename, cfg_filename=None, use_sbml_names=False, cmaboss=False):
"""Loads a network from a SBML format file.
:param str sbml_filename: Network file
:param str cfg_filename: Configuraton file
:param bool use_sbml_names: Use SBML names instead of IDs to name nodes
:rtype: :py:class:`.Simulation`
"""
assert sbml_filename.lower().endswith(".xml") or sbml_filename.lower().endswith(".sbml")
if "://" in sbml_filename:
from colomoto_jupyter.io import ensure_localfile
sbml_filename = ensure_localfile(sbml_filename)
if cmaboss:
return SBMLSSimulation(sbml_filename, cfg_filename, use_sbml_names)
return sbml_to_maboss(sbml_filename, cfg_filename, use_sbml_names)
[docs]
def load(bnd_filename, *cfg_filenames, **extra_args):
"""Loads a network from a MaBoSS format file.
:param str bnd_filename: Network file
:param str cfg_filename: Configuraton file
:rtype: :py:class:`.Simulation`
"""
assert bnd_filename.lower().endswith(".bnd"), "wrong extension for bnd file"
if "://" in bnd_filename:
from colomoto_jupyter.io import ensure_localfile
bnd_filename = ensure_localfile(bnd_filename)
if not cfg_filenames:
cfg_filenames = [".".join([".".join(bnd_filename.split(".")[:-1]), "cfg"])]
elif "://" in " ".join(cfg_filenames):
from colomoto_jupyter.io import ensure_localfile
cfg_filenames = [ensure_localfile(cfg_filename) if "://" in cfg_filename else cfg_filename for cfg_filename in cfg_filenames]
if extra_args.get("cmaboss"):
return CMaBoSSSimulation(bnd_filename, *cfg_filenames)
command = extra_args.get("command")
with ExitStack() as stack:
bnd_file = stack.enter_context(open(bnd_filename, 'r'))
bnd_content = bnd_file.read()
cfg_content = ""
for cfg_filename in tuple(cfg_filenames):
cfg_file = stack.enter_context(open(cfg_filename, 'r'))
cfg_content += cfg_file.read()
(variables, parameters, is_internal_list, in_graph_list,
istate_list, refstate_list, schedule_list) = _read_cfg(cfg_content)
nodes, mutations = _read_bnd(bnd_content, is_internal_list, in_graph_list)
mutationTypes = {}
for mutation in mutations:
if ("High_%s" % mutation) in variables and int(variables["High_%s" % mutation]) == 1:
mutationTypes[mutation] = "ON"
elif ("Low_%s" % mutation) in variables and int(variables["Low_%s" % mutation]) == 1:
mutationTypes[mutation] = "OFF"
mutations = list(mutationTypes.keys())
net = Network(nodes)
for name in schedule_list.keys():
net[name].set_schedule(schedule_list[name])
for istate in istate_list:
net.set_istate(istate, istate_list[istate])
for v in variables:
lhs = '$'+v
parameters[lhs] = variables[v]
ret = Simulation(net, parameters, command=command, mutations=mutations, mutationsTypes=mutationTypes)
ret.refstate = refstate_list
return ret
def _read_cfg(string):
variables = OrderedDict()
parameters = OrderedDict()
is_internal_list = {}
in_graph_list = OrderedDict()
istate_list = OrderedDict()
schedule_list = OrderedDict()
refstate_list = {}
parse_cfg = cfg_grammar.parse_string(string)
for token in parse_cfg:
if token.lhs: # True if token is var_decl
variables[token.lhs] = token.rhs
if token.get_name() == "internal": # True if token is internal_decls
is_internal_list[token.internal] = token.is_internal_val
if token.get_name() == "ingraph": # True if token is ingraph_decl
in_graph_list[token.ingraph] = token.in_graph_val
if token.get_name() == "refstate": # True if token is refstate_decl
refstate_list[token.refstate] = token.refstate_val
if token.get_name() == "schedule": # True if token is schedule_decl
t_schedule = OrderedDict()
for i in range(0, len(token.schedule_val),2):
try:
t_schedule[float(token.schedule_val[i])] = float(token.schedule_val[i+1])
except ValueError:
t_schedule[float(token.schedule_val[i])] = str(token.schedule_val[i+1])
schedule_list[token.schedule] = t_schedule
if token.param: # True if token is param_decl
parameters[token.param] = float(token.value)
if token.nd_i:
istate_list[token.nd_i] = {0: 1 - int(token.istate_val),
1: int(token.istate_val)}
if token.attrib: # True if token is istate_decl
# TODO check if lens are consistent
if len(token.nodes) == 1:
t_istate_list = OrderedDict()
for t in token.attrib:
try:
t_istate_list[int(t[1][0])] = float(str(t[0]))
except ValueError:
t_istate_list[int(t[1][0])] = str(t[0])
istate_list[token.nodes[0]] = t_istate_list
else:
nodes = tuple(token.nodes)
t_istate_list = {}
for t in token.attrib:
try:
t_istate_list[tuple(t[1])] = float(str(t[0]))
except ValueError:
t_istate_list[tuple(t[1])] = str(t[0])
istate_list[nodes] = t_istate_list
return (variables, parameters, is_internal_list, in_graph_list, istate_list,
refstate_list, schedule_list)
def _read_bnd(string, is_internal_list, in_graph_list):
nodes = []
parse_bnd = bnd_grammar.parse_string(string)
mutations = []
for token in parse_bnd:
interns = {v.lhs: v.rhs for v in token.interns}
logic = interns.pop('logic') if 'logic' in interns else None
rate_up = interns.pop('rate_up') if 'rate_up' in interns.keys() else "@logic ? 1.0 : 0.0"
rate_down = interns.pop('rate_down') if 'rate_down' in interns.keys() else "@logic ? 0.0 : 1.0"
pattern_up = r'\$Low_%s \? 0 : \(\$High_%s \? 1E308/\$nb_mutable : \((.*)\)\)' % (token.name, token.name)
pattern_down = r'\$High_%s \? 0 : \(\$Low_%s \? 1E308/\$nb_mutable : \((.*)\)\)' % (token.name, token.name)
res_search_up = re.search(pattern_up, rate_up)
res_search_down = re.search(pattern_down, rate_down)
if res_search_up is not None and res_search_down is not None:
mutations.append(token.name)
internal = (is_internal_list[token.name]
if token.name in is_internal_list
else False)
ingraph = (in_graph_list[token.name]
if token.name in in_graph_list
else False)
nodes.append(Node(token.name, logic, rate_up, rate_down,
internal, interns, ingraph))
return nodes, mutations