"""
Behavioral dynamics of component types.
@author: Gautham Ganapathy
@organization: LEMS (http://neuroml.org/lems/, https://github.com/organizations/LEMS)
@contact: gautham@lisphacker.org
MAvdVlag: altered attributes for state_variables, derived_variables, time_derivatives and
conditional_derived_variable.
"""
from ..base.base import LEMSBase
from ..base.map import Map
from ..base.errors import ModelError,ParseError
from ..parser.expr import ExprParser
[docs]class StateVariable(LEMSBase):
"""
Store the specification of a state variable.
"""
def __init__(self, name, default, boundaries = None):
"""
Constructor.
See instance variable documentation for more info on parameters.
"""
self.name = name
""" Name of the state variable.
@type: str """
self.default = default
""" Dimension of the state variable.
@type: str """
self.boundaries = boundaries
""" Exposure name for the state variable.
@type: str """
def __str__(self):
return 'StateVariable name="{0}" dimension="{1}"'.format(self.name, self.default) +\
(' exposure="{0}"'.format(self.boundaries) if self.boundaries else '')
[docs] def toxml(self):
"""
Exports this object into a LEMS XML object
"""
return '<StateVariable name="{0}" dimension="{1}"'.format(self.name, self.dimension) +\
(' exposure="{0}"'.format(self.exposure) if self.exposure else '') +\
'/>'
[docs]class DerivedVariable(LEMSBase):
"""
Store the specification of a derived variable.
"""
def __init__(self, name, **params):
"""
Constructor.
See instance variable documentation for more info on parameters.
"""
self.name = name
""" Name of the derived variable.
@type: str """
self.dimension = params['dimension'] if 'dimension' in params else None
""" Dimension of the derived variable or None if computed.
@type: str """
self.exposure = params['exposure'] if 'exposure' in params else None
""" Exposure name for the derived variable.
@type: str """
self.select = params['select'] if 'select' in params else None
""" Selection path/expression for the derived variable.
@type: str """
self.expression = params['expression'] if 'expression' in params else None
""" Value of the derived variable.
@type: str """
self.reduce = params['reduce'] if 'reduce' in params else None
""" Reduce method for the derived variable.
@type: str """
self.required = params['required'] if 'required' in params else None
""" Requried or not.
@type: str """
self.expression_tree = None
""" Parse tree for the time derivative expression.
@type: lems.parser.expr.ExprNode """
if self.expression != None:
try:
self.expression_tree = ExprParser(self.expression).parse()
except:
raise ParseError("Parse error when parsing value expression "
"'{0}' for derived variable {1}",
self.expression, self.name)
[docs] def toxml(self):
"""
Exports this object into a LEMS XML object
"""
return '<DerivedVariable name="{0}"'.format(self.name) +\
(' dimension="{0}"'.format(self.dimension) if self.dimension else '') +\
(' exposure="{0}"'.format(self.exposure) if self.exposure else '') +\
(' select="{0}"'.format(self.select) if self.select else '') +\
(' value="{0}"'.format(self.value) if self.value else '') +\
(' reduce="{0}"'.format(self.reduce) if self.reduce else '') +\
(' required="{0}"'.format(self.required) if self.required else '') +\
'/>'
[docs]class Case(LEMSBase):
"""
Store the specification of a case for a Conditional Derived Variable.
"""
def __init__(self, condition, value):
"""
Constructor.
"""
self.condition = condition
""" Condition for this case.
@type: str """
self.value = value
""" Value if the condition is true.
@type: str """
self.condition_expression_tree = None
""" Parse tree for the case condition expression.
@type: lems.parser.expr.ExprNode """
self.value_expression_tree = None
""" Parse tree for the case condition expression.
@type: lems.parser.expr.ExprNode """
try:
self.value_expression_tree = ExprParser(self.value).parse()
if not self.condition:
self.condition_expression_tree = None
else:
self.condition_expression_tree = ExprParser(self.condition).parse()
except:
raise ParseError("Parse error when parsing case with condition "
"'{0}' and value {1}",
self.condition, self.value)
[docs] def toxml(self):
"""
Exports this object into a LEMS XML object
"""
return '<Case condition="{0}" value="{1}"'.format(self.condition, self.value) + '/>'
[docs]class ConditionalDerivedVariable(LEMSBase):
"""
Store the specification of a conditional derived variable.
"""
def __init__(self, name, condition, exposure = None, cases = None):
"""
Constructor.
See instance variable documentation for more info on parameters.
"""
self.name = name
""" Name of the derived variable.
@type: str """
self.condition = condition
""" Dimension of the state variable.
@type: str """
self.exposure = exposure
""" Exposure name for the state variable.
@type: str """
self.cases = list(cases.split(", "))
""" List of cases related to this conditional derived variable.
@type: list(lems.model.dynamics.Case) """
[docs] def add_case(self, case):
"""
Adds a case to this conditional derived variable.
@param case: Case to be added.
@type case: lems.model.dynamics.Case
"""
self.cases.append(case)
[docs] def add(self, child):
"""
Adds a typed child object to the conditional derived variable.
@param child: Child object to be added.
"""
if isinstance(child, Case):
self.add_case(child)
else:
raise ModelError('Unsupported child element')
[docs] def toxml(self):
"""
Exports this object into a LEMS XML object
"""
xmlstr = '<ConditionalDerivedVariable name="{0}"'.format(self.name) +\
(' dimension="{0}"'.format(self.dimension) if self.dimension else '') +\
(' exposure="{0}"'.format(self.exposure) if self.exposure else '')
chxmlstr = ''
for case in self.cases:
chxmlstr += case.toxml()
if chxmlstr:
xmlstr += '>' + chxmlstr + '</ConditionalDerivedVariable>'
else:
xmlstr += '/>'
return xmlstr
[docs]class TimeDerivative(LEMSBase):
"""
Store the specification of a time derivative specifcation.
"""
def __init__(self, name, expression):
"""
Constructor.
See instance variable documentation for more info on parameters.
"""
self.name = name
""" Name of the variable for which the time derivative is being specified.
@type: str """
self.expression = expression
""" Derivative expression.
@type: str """
self.expression_tree = None
""" Parse tree for the time derivative expression.
@type: lems.parser.expr.ExprNode """
try:
self.expression_tree = ExprParser(expression).parse()
except:
raise ParseError("Parse error when parsing value expression "
"'{0}' for state variable {1}",
self.expression, self.name)
[docs] def toxml(self):
"""
Exports this object into a LEMS XML object
"""
return '<TimeDerivative variable="{0}" value="{1}"/>'.format(self.variable, self.value)
[docs]class Action(LEMSBase):
"""
Base class for event handler actions.
"""
pass
[docs]class StateAssignment(Action):
"""
State assignment specification.
"""
def __init__(self, variable, value):
"""
Constructor.
See instance variable documentation for more info on parameters.
"""
Action.__init__(self)
self.variable = variable
""" Name of the variable for which the time derivative is being specified.
@type: str """
self.value = value
""" Derivative expression.
@type: str """
self.expression_tree = None
""" Parse tree for the time derivative expression.
@type: lems.parser.expr.ExprNode """
try:
self.expression_tree = ExprParser(value).parse()
except:
raise ParseError("Parse error when parsing state assignment "
"value expression "
"'{0}' for state variable {1}",
self.value, self.variable)
[docs] def toxml(self):
"""
Exports this object into a LEMS XML object
"""
return '<StateAssignment variable="{0}" value="{1}"/>'.format(self.variable, self.value)
[docs]class EventOut(Action):
"""
Event transmission specification.
"""
def __init__(self, port):
"""
Constructor.
See instance variable documentation for more details on parameters.
"""
Action.__init__(self)
self.port = port
""" Port on which the event comes in.
@type: str """
[docs] def toxml(self):
"""
Exports this object into a LEMS XML object
"""
return '<EventOut port="{0}"/>'.format(self.port)
[docs]class Transition(Action):
"""
Regime transition specification.
"""
def __init__(self, regime):
"""
Constructor.
See instance variable documentation for more details on parameters.
"""
Action.__init__(self)
self.regime = regime
""" Regime to transition to.
@type: str """
[docs] def toxml(self):
"""
Exports this object into a LEMS XML object
"""
return '<Transition regime="{0}"/>'.format(self.regime)
[docs]class EventHandler(LEMSBase):
"""
Base class for event handlers.
"""
def __init__(self):
"""
Constructor.
"""
self.actions = list()
""" List of actions to be performed in response to this event.
@type: list(lems.model.dynamics.Action) """
def __str__(self):
istr = 'EventHandler...'
return istr
[docs] def add_action(self, action):
"""
Adds an action to this event handler.
@param action: Action to be added.
@type action: lems.model.dynamics.Action
"""
self.actions.append(action)
[docs] def add(self, child):
"""
Adds a typed child object to the event handler.
@param child: Child object to be added.
"""
if isinstance(child, Action):
self.add_action(child)
else:
raise ModelError('Unsupported child element')
[docs]class OnStart(EventHandler):
"""
Specification for event handler called upon initialization of the component.
"""
def __init__(self):
"""
Constructor.
"""
EventHandler.__init__(self)
def __str__(self):
istr = 'OnStart: ['
for action in self.actions:
istr += str(action)
istr += ']'
return str(istr)
[docs] def toxml(self):
"""
Exports this object into a LEMS XML object
"""
xmlstr = '<OnStart'
chxmlstr = ''
for action in self.actions:
chxmlstr += action.toxml()
if chxmlstr:
xmlstr += '>' + chxmlstr + '</OnStart>'
else:
xmlstr += '/>'
return xmlstr
[docs]class OnCondition(EventHandler):
"""
Specification for event handler called upon satisfying a given condition.
"""
def __init__(self, test):
"""
Constructor.
See instance variable documentation for more details on parameters.
"""
EventHandler.__init__(self)
self.test = test
""" Condition to be tested for.
@type: str """
try:
self.expression_tree = ExprParser(test).parse()
except:
raise ParseError("Parse error when parsing OnCondition test '{0}'", test)
def __str__(self):
istr = 'OnCondition...'
return istr
[docs] def toxml(self):
"""
Exports this object into a LEMS XML object
"""
xmlstr = '<OnCondition test="{0}"'.format(self.test)
chxmlstr = ''
for action in self.actions:
chxmlstr += action.toxml()
if chxmlstr:
xmlstr += '>' + chxmlstr + '</OnCondition>'
else:
xmlstr += '/>'
return xmlstr
[docs]class OnEvent(EventHandler):
"""
Specification for event handler called upon receiving en event sent by another component.
"""
def __init__(self, port):
"""
Constructor.
See instance variable documentation for more details on parameters.
"""
EventHandler.__init__(self)
self.port = port
""" Port on which the event comes in.
@type: str """
def __str__(self):
istr = 'OnEvent, port: %s'%self.port
return istr
[docs] def toxml(self):
"""
Exports this object into a LEMS XML object
"""
xmlstr = '<OnEvent port="{0}"'.format(self.port)
chxmlstr = ''
for action in self.actions:
chxmlstr += action.toxml()
if chxmlstr:
xmlstr += '>' + chxmlstr + '</OnEvent>'
else:
xmlstr += '/>'
return xmlstr
[docs]class OnEntry(EventHandler):
"""
Specification for event handler called upon entry into a new behavior regime.
"""
def __init__(self):
"""
Constructor.
"""
EventHandler.__init__(self)
[docs] def toxml(self):
"""
Exports this object into a LEMS XML object
"""
xmlstr = '<OnEntry'
chxmlstr = ''
for action in self.actions:
chxmlstr += action.toxml()
if chxmlstr:
xmlstr += '>' + chxmlstr + '</OnEntry>'
else:
xmlstr += '/>'
return xmlstr
[docs]class KineticScheme(LEMSBase):
"""
Kinetic scheme specifications.
"""
def __init__(self, name, nodes, state_variable,
edges, edge_source, edge_target,
forward_rate, reverse_rate):
"""
Constructor.
See instance variable documentation for more details on parameters.
"""
self.name = name
""" Name of the kinetic scheme.
@type: str """
self.nodes = nodes
""" Nodes to be used for the kinetic scheme.
@type: str """
self.state_variable = state_variable
""" State variable updated by the kinetic scheme.
@type: str """
self.edges = edges
""" Edges to be used for the kinetic scheme.
@type: str """
self.edge_source = edge_source
""" Attribute that defines the source of the transition.
@type: str """
self.edge_target = edge_target
""" Attribute that defines the target of the transition.
@type: str """
self.forward_rate = forward_rate
""" Name of the forward rate exposure.
@type: str """
self.reverse_rate = reverse_rate
""" Name of the reverse rate exposure.
@type: str """
[docs] def toxml(self):
"""
Exports this object into a LEMS XML object
"""
return ('<KineticScheme '
'name="{0}" '
'nodes="{1}" '
'edges="{2}" '
'stateVariable="{3}" '
'edgeSource="{4}" '
'edgeTarget="{5}" '
'forwardRate="{6}" '
'reverseRate="{7}"/>').format(self.name,
self.nodes,
self.edges,
self.state_variable,
self.edge_source,
self.edge_target,
self.forward_rate,
self.reverse_rate)
[docs]class Behavioral(LEMSBase):
"""
Store dynamic behavioral attributes.
"""
def __init__(self):
"""
Constructor.
See instance variable documentation for more details on parameters.
"""
self.parent_behavioral = None
""" Parent behavioral object.
@type: lems.model.dynamics.Behavioral """
self.state_variables = Map()
""" Map of state variables in this behavior regime.
@type: dict(str -> lems.model.dynamics.StateVariable """
self.derived_variables = Map()
""" Map of derived variables in this behavior regime.
@type: dict(str -> lems.model.dynamics.DerivedVariable """
self.conditional_derived_variables = Map()
""" Map of conditional derived variables in this behavior regime.
@type: dict(str -> lems.model.dynamics.ConditionalDerivedVariable """
self.time_derivatives = Map()
""" Map of time derivatives in this behavior regime.
@type: dict(str -> lems.model.dynamics.TimeDerivative) """
self.event_handlers = list()
""" List of event handlers in this behavior regime.
@type: list(lems.model.dynamics.EventHandler) """
self.kinetic_schemes = Map()
""" Map of kinetic schemes in this behavior regime.
@type: dict(str -> lems.model.dynamics.KineticScheme) """
[docs] def has_content(self):
if len(self.state_variables)==0 and \
len(self.derived_variables)==0 and \
len(self.conditional_derived_variables)==0 and \
len(self.time_derivatives)==0 and \
len(self.event_handlers)==0 and \
len(self.kinetic_schemes)==0:
return False
else:
return True
[docs] def clear(self):
"""
Clear behavioral entities.
"""
self.time_derivatives = Map()
[docs] def add_state_variable(self, sv):
"""
Adds a state variable to this behavior regime.
@param sv: State variable.
@type sv: lems.model.dynamics.StateVariable
"""
self.state_variables[sv.name] = sv
[docs] def add_derived_variable(self, dv):
"""
Adds a derived variable to this behavior regime.
@param dv: Derived variable.
@type dv: lems.model.dynamics.DerivedVariable
"""
self.derived_variables[dv.name] = dv
[docs] def add_conditional_derived_variable(self, cdv):
"""
Adds a conditional derived variable to this behavior regime.
@param cdv: Conditional Derived variable.
@type cdv: lems.model.dynamics.ConditionalDerivedVariable
"""
self.conditional_derived_variables[cdv.name] = cdv
[docs] def add_time_derivative(self, td):
"""
Adds a time derivative to this behavior regime.
@param td: Time derivative.
@type td: lems.model.dynamics.TimeDerivative
"""
self.time_derivatives[td.name] = td
[docs] def add_event_handler(self, eh):
"""
Adds an event handler to this behavior regime.
@param eh: Event handler.
@type eh: lems.model.dynamics.EventHandler
"""
self.event_handlers.append(eh)
[docs] def add_kinetic_scheme(self, ks):
"""
Adds a kinetic scheme to this behavior regime.
@param ks: Kinetic scheme.
@type ks: lems.model.dynamics.KineticScheme
"""
self.kinetic_schemes[ks.name] = ks
[docs] def add(self, child):
"""
Adds a typed child object to the behavioral object.
@param child: Child object to be added.
"""
if isinstance(child, StateVariable):
self.add_state_variable(child)
elif isinstance(child, DerivedVariable):
self.add_derived_variable(child)
elif isinstance(child, ConditionalDerivedVariable):
self.add_conditional_derived_variable(child)
elif isinstance(child, TimeDerivative):
self.add_time_derivative(child)
elif isinstance(child, EventHandler):
self.add_event_handler(child)
elif isinstance(child, KineticScheme):
self.add_kinetic_scheme(child)
else:
raise ModelError('Unsupported child element')
[docs] def toxml(self):
"""
Exports this object into a LEMS XML object
"""
chxmlstr = ''
for state_variable in self.state_variables:
chxmlstr += state_variable.toxml()
for derived_variable in self.derived_variables:
chxmlstr += derived_variable.toxml()
for conditional_derived_variable in self.conditional_derived_variables:
chxmlstr += conditional_derived_variable.toxml()
for time_derivative in self.time_derivatives:
chxmlstr += time_derivative.toxml()
for event_handler in self.event_handlers:
chxmlstr += event_handler.toxml()
for kinetic_scheme in self.kinetic_schemes:
chxmlstr += kinetic_scheme.toxml()
if isinstance(self, Dynamics):
for regime in self.regimes:
chxmlstr += regime.toxml()
if isinstance(self, Dynamics):
xmlprefix = 'Dynamics'
xmlsuffix = 'Dynamics'
xmlempty = ''
else:
xmlprefix = 'Regime name="{0}"'.format(self.name) +\
(' initial="true"' if self.initial else '')
xmlsuffix = 'Regime'
xmlempty = '<{0}/>',format(xmlprefix)
if chxmlstr:
xmlstr = '<{0}>'.format(xmlprefix) + chxmlstr + '</{0}>'.format(xmlsuffix)
else:
xmlstr = xmlempty
return xmlstr
[docs]class Regime(Behavioral):
"""
Stores a single behavioral regime for a component type.
"""
def __init__(self, name, parent_behavioral, initial = False):
"""
Constructor.
See instance variable documentation for more details on parameters.
"""
Behavioral.__init__(self)
self.name = name
""" Name of this behavior regime.
@type: str """
self.parent_behavioral = parent_behavioral
""" Parent behavioral object.
@type: lems.model.dynamics.Behavioral """
self.initial = initial
""" Initial behavior regime.
@type: bool """
[docs]class Dynamics(Behavioral):
"""
Stores behavioral dynamics specification for a component type.
"""
def __init__(self):
"""
Constructor.
"""
Behavioral.__init__(self)
self.regimes = Map()
""" Map of behavior regimes.
@type: Map(str -> lems.model.dynamics.Regime) """
[docs] def add_regime(self, regime):
"""
Adds a behavior regime to this dynamics object.
@param regime: Behavior regime to be added.
@type regime: lems.model.dynamics.Regime """
self.regimes[regime.name] = regime
[docs] def add(self, child):
"""
Adds a typed child object to the dynamics object.
@param child: Child object to be added.
"""
if isinstance(child, Regime):
self.add_regime(child)
else:
Behavioral.add(self, child)
[docs] def has_content(self):
if len(self.regimes)>0:
return True
else:
return Behavioral.has_content(self)