def test_assert_no_duplicates(self): # Signature: name(lst, error_func=None) # Check for duplicates in a sequence. # # This function checks that a list contains no duplicates, by casting the list # to a set and comparing the lengths. # # It raises an `NineMLRuntimeError` if the lengths are not equal. # Duplication self.assertRaises( NineMLRuntimeError, assert_no_duplicates, [1, 2, 3, 4, 4], ) self.assertRaises( NineMLRuntimeError, assert_no_duplicates, ['1', '2', '3', '4', '4'], ) assert_no_duplicates([1, 2, 3, 4]) assert_no_duplicates(['1', '2', '3', '4']) assert_no_duplicates([None]) assert_no_duplicates([True]) assert_no_duplicates([()])
def test_assert_no_duplicates(self): # Signature: name(lst, error_func=None) # Check for duplicates in a sequence. # # This function checks that a list contains no duplicates, by casting the list # to a set and comparing the lengths. # # It raises an `NineMLUsageError` if the lengths are not equal. # Duplication self.assertRaises( NineMLUsageError, assert_no_duplicates, [1, 2, 3, 4, 4], ) self.assertRaises( NineMLUsageError, assert_no_duplicates, ['1', '2', '3', '4', '4'], ) assert_no_duplicates([1, 2, 3, 4]) assert_no_duplicates(['1', '2', '3', '4']) assert_no_duplicates([None]) assert_no_duplicates([True]) assert_no_duplicates([()])
def __init__(self, *args, **kwargs): BaseALObject.__init__(self) ContainerObject.__init__(self) valid_kwargs = ('name', 'transitions', 'on_events', 'on_conditions', 'time_derivatives', 'aliases') for arg in kwargs: if arg not in valid_kwargs: err = 'Unexpected Arg: %s' % arg raise NineMLUsageError(err) name = kwargs.get('name', None) if name is None: self._name = 'default' else: self._name = validate_identifier(name) validate_identifier(self._name) # Get Time derivatives from args or kwargs kw_tds = normalise_parameter_as_list( kwargs.get('time_derivatives', None)) time_derivatives = list(args) + kw_tds # Un-named arguments are time_derivatives: time_derivatives = normalise_parameter_as_list(time_derivatives) # time_derivatives.extend( args ) td_types = (basestring, TimeDerivative) td_type_dict = filter_discrete_types(time_derivatives, td_types) td_from_str = [TimeDerivative.from_str(o) for o in td_type_dict[basestring]] time_derivatives = td_type_dict[TimeDerivative] + td_from_str # Check for double definitions: td_dep_vars = [td.variable for td in time_derivatives] assert_no_duplicates( td_dep_vars, ("Multiple time derivatives found for the same state variable " "in regime '{}' (found '{}')".format( self.name, "', '".join(td.variable for td in time_derivatives)))) self.add(*time_derivatives) # We support passing in 'transitions', which is a list of both OnEvents # and OnConditions as well as separate on_conditions and on_events. transitions = normalise_parameter_as_list( kwargs.get('transitions', None)) self.add(*transitions) on_conditions = normalise_parameter_as_list( kwargs.get('on_conditions', None)) self.add(*on_conditions) on_events = normalise_parameter_as_list( kwargs.get('on_events', None)) self.add(*on_events) # Add regime specific aliases aliases = normalise_parameter_as_list(kwargs.get('aliases', None)) self.add(*aliases)
def _resolve_transition_regime_names(self): # Check that the names of the regimes are unique: names = [r.name for r in self.regimes] assert_no_duplicates(names) # Create a map of regime names to regimes: regime_map = dict([(r.name, r) for r in self.regimes]) # We only worry about 'target' regimes, since source regimes are taken # care of for us by the Regime objects they are attached to. for trans in self.transitions: if trans.target_regime_name not in regime_map: raise NineMLRuntimeError( "Can't find regime '{}'".format(trans.target_regime_name)) trans.set_target_regime(regime_map[trans.target_regime_name])
def _resolve_transition_regime_names(self): # Check that the names of the regimes are unique: names = [r.name for r in self.regimes] assert_no_duplicates(names) # Create a map of regime names to regimes: regime_map = dict([(r.name, r) for r in self.regimes]) # We only worry about 'target' regimes, since source regimes are taken # care of for us by the Regime objects they are attached to. for trans in self.transitions: if trans.target_regime_name not in regime_map: raise NineMLRuntimeError("Can't find regime '{}'".format( trans.target_regime_name)) trans.set_target_regime(regime_map[trans.target_regime_name])
def _resolve_transition_regimes(self): # Check that the names of the regimes are unique: assert_no_duplicates([r.name for r in self.regimes]) # We only worry about 'target' regimes, since source regimes are taken # care of for us by the Regime objects they are attached to. for regime in self.regimes: for trans in regime.transitions: trans.set_source_regime(regime) target = trans.target_regime_name if target is None: target = regime # to same regime else: try: target = self.regime(target) # Lookup by name except KeyError: self.regime(target) raise NineMLUsageError( "Can't find regime '{}' referenced from '{}' " "transition".format(trans.target_regime, trans.key)) trans.set_target_regime(target)
def __init__(self, regimes=None, aliases=None, state_variables=None): """DynamicsBlock object constructor :param aliases: A list of aliases, which must be either |Alias| objects or ``string``s. :param regimes: A list containing at least one |Regime| object. :param state_variables: An optional list of the state variables, which can either be |StateVariable| objects or `string` s. If provided, it must match the inferred state-variables from the regimes; if it is not provided it will be inferred automatically. """ aliases = normalise_parameter_as_list(aliases) regimes = normalise_parameter_as_list(regimes) state_variables = normalise_parameter_as_list(state_variables) # Load the aliases as objects or strings: alias_td = filter_discrete_types(aliases, (basestring, Alias)) aliases_from_strs = [Alias.from_str(o) for o in alias_td[basestring]] aliases = alias_td[Alias] + aliases_from_strs # Load the state variables as objects or strings: sv_types = (basestring, StateVariable) sv_td = filter_discrete_types(state_variables, sv_types) sv_from_strings = [ StateVariable(o, dimension=None) for o in sv_td[basestring] ] state_variables = sv_td[StateVariable] + sv_from_strings assert_no_duplicates(r.name for r in regimes) assert_no_duplicates(a.lhs for a in aliases) assert_no_duplicates(s.name for s in state_variables) self._regimes = dict((r.name, r) for r in regimes) self._aliases = dict((a.lhs, a) for a in aliases) self._state_variables = dict((s.name, s) for s in state_variables)
def __init__(self, regimes=None, aliases=None, state_variables=None): """DynamicsBlock object constructor :param aliases: A list of aliases, which must be either |Alias| objects or ``string``s. :param regimes: A list containing at least one |Regime| object. :param state_variables: An optional list of the state variables, which can either be |StateVariable| objects or `string` s. If provided, it must match the inferred state-variables from the regimes; if it is not provided it will be inferred automatically. """ aliases = normalise_parameter_as_list(aliases) regimes = normalise_parameter_as_list(regimes) state_variables = normalise_parameter_as_list(state_variables) # Load the aliases as objects or strings: alias_td = filter_discrete_types(aliases, (basestring, Alias)) aliases_from_strs = [Alias.from_str(o) for o in alias_td[basestring]] aliases = alias_td[Alias] + aliases_from_strs # Load the state variables as objects or strings: sv_types = (basestring, StateVariable) sv_td = filter_discrete_types(state_variables, sv_types) sv_from_strings = [StateVariable(o, dimension=None) for o in sv_td[basestring]] state_variables = sv_td[StateVariable] + sv_from_strings assert_no_duplicates(r.name for r in regimes) assert_no_duplicates(a.lhs for a in aliases) assert_no_duplicates(s.name for s in state_variables) self._regimes = dict((r.name, r) for r in regimes) self._aliases = dict((a.lhs, a) for a in aliases) self._state_variables = dict((s.name, s) for s in state_variables)
def __init__(self, name, parameters=None, analog_ports=[], event_ports=[], dynamicsblock=None, subnodes=None, portconnections=None, regimes=None, aliases=None, state_variables=None): """Constructs a DynamicsClass :param name: The name of the componentclass. :param parameters: A list containing either |Parameter| objects or strings representing the parameter names. If ``None``, then the parameters are automatically inferred from the |Dynamics| block. :param analog_ports: A list of |AnalogPorts|, which will be the local |AnalogPorts| for this object. :param event_ports: A list of |EventPorts| objects, which will be the local event-ports for this object. If this is ``None``, then they will be automatically inferred from the dynamics block. :param dynamicsblock: A |DynamicsBlock| object, defining the local dynamicsblock of the componentclass. :param subnodes: A dictionary mapping namespace-names to sub- componentclass. [Type: ``{string:|DynamicsClass|, string:|DynamicsClass|, string:|DynamicsClass|}`` ] describing the namespace of subcomponents for this componentclass. :param portconnections: A list of pairs, specifying the connections between the ports of the subcomponents in this componentclass. These can be `(|NamespaceAddress|, |NamespaceAddress|)' or ``(string, string)``. :param interface: A shorthand way of specifying the **interface** for this componentclass; |Parameters|, |AnalogPorts| and |EventPorts|. ``interface`` takes a list of these objects, and automatically resolves them by type into the correct types. Examples: >>> a = DynamicsClass(name='MyComponent1') .. todo:: Point this towards and example of constructing ComponentClasses. This can't be here, because we also need to know about dynamics. For examples """ # We can specify in the componentclass, and they will get forwarded to # the dynamics class. We check that we do not specify half-and-half: if dynamicsblock is not None: if regimes or aliases or state_variables: err = "Either specify a 'dynamicsblock' parameter, or " err += "state_variables /regimes/aliases, but not both!" raise NineMLRuntimeError(err) else: dynamicsblock = DynamicsBlock(regimes=regimes, aliases=aliases, state_variables=state_variables) ComponentClass.__init__(self, name, parameters, main_block=dynamicsblock) self._query = DynamicsQueryer(self) # Ensure analog_ports is a list not an iterator analog_ports = list(analog_ports) event_ports = list(event_ports) # Check there aren't any duplicates in the port and parameter names assert_no_duplicates(p if isinstance(p, basestring) else p.name for p in chain(parameters if parameters else [], analog_ports, event_ports)) self._analog_send_ports = dict( (p.name, p) for p in analog_ports if isinstance(p, AnalogSendPort)) self._analog_receive_ports = dict( (p.name, p) for p in analog_ports if isinstance(p, AnalogReceivePort)) self._analog_reduce_ports = dict( (p.name, p) for p in analog_ports if isinstance(p, AnalogReducePort)) # Create dummy event ports to keep the ActionVisitor base class of # the interface inferrer happy self._event_receive_ports = self._event_send_ports = self.subnodes = {} # EventPort, StateVariable and Parameter Inference: inferred_struct = DynamicsClassInterfaceInferer(self) # Check any supplied parameters match: if parameters is not None: inf_check(self._parameters.keys(), inferred_struct.parameter_names, 'Parameters') else: self._parameters = dict((n, Parameter(n)) for n in inferred_struct.parameter_names) # Check any supplied state_variables match: if self.dynamicsblock._state_variables: state_var_names = [p.name for p in self.dynamicsblock.state_variables] inf_check(state_var_names, inferred_struct.state_variable_names, 'StateVariables') else: state_vars = dict((n, StateVariable(n)) for n in inferred_struct.state_variable_names) self.dynamicsblock._state_variables = state_vars # Set and check event receive ports match inferred self._event_receive_ports = dict( (p.name, p) for p in event_ports if isinstance(p, EventReceivePort)) if len(self._event_receive_ports): # FIXME: not all OutputEvents are necessarily exposed as Ports, # so really we should just check that all declared output event # ports are in the list of inferred ports, not that the declared # list is identical to the inferred one. inf_check(self._event_receive_ports.keys(), inferred_struct.input_event_port_names, 'Event Ports In') else: # FIXME: TGC don't like this shorthand # Event ports not supplied, so lets use the inferred ones. for pname in inferred_struct.input_event_port_names: self._event_receive_ports[pname] = EventReceivePort(name=pname) # Set and check event send ports match inferred self._event_send_ports = dict( (p.name, p) for p in event_ports if isinstance(p, EventSendPort)) if len(self._event_send_ports): inf_check(self._event_send_ports.keys(), inferred_struct.event_out_port_names, 'Event Ports Out') else: # Event ports not supplied, so lets use the inferred ones. for pname in inferred_struct.event_out_port_names: self._event_send_ports[pname] = EventSendPort(name=pname) # Call namespace mixin constructor _NamespaceMixin.__init__( self, subnodes=subnodes, portconnections=portconnections) # Finalise initiation: self._resolve_transition_regime_names() # Store flattening Information: self._flattener = None # Is the finished componentclass valid?: self._validate_self()
def __init__(self, *args, **kwargs): """Regime constructor :param name: The name of the constructor. If none, then a name will be automatically generated. :param time_derivatives: A list of time derivatives, as either ``string``s (e.g 'dg/dt = g/gtau') or as |TimeDerivative| objects. :param transitions: A list containing either |OnEvent| or |OnCondition| objects, which will automatically be sorted into the appropriate classes automatically. :param *args: Any non-keyword arguments will be treated as time_derivatives. """ valid_kwargs = ('name', 'transitions', 'time_derivatives') for arg in kwargs: if arg not in valid_kwargs: err = 'Unexpected Arg: %s' % arg raise NineMLRuntimeError(err) transitions = kwargs.get('transitions', None) name = kwargs.get('name', None) kw_tds = normalise_parameter_as_list(kwargs.get('time_derivatives', None)) time_derivatives = list(args) + kw_tds self._name = name if self.name is not None: self._name = self._name.strip() ensure_valid_identifier(self._name) # Un-named arguments are time_derivatives: time_derivatives = normalise_parameter_as_list(time_derivatives) # time_derivatives.extend( args ) td_types = (basestring, TimeDerivative) td_type_dict = filter_discrete_types(time_derivatives, td_types) td_from_str = [TimeDerivative.from_str(o) for o in td_type_dict[basestring]] time_derivatives = td_type_dict[TimeDerivative] + td_from_str # Check for double definitions: td_dep_vars = [td.dependent_variable for td in time_derivatives] assert_no_duplicates(td_dep_vars) # Store as a dictionary self._time_derivatives = dict((td.dependent_variable, td) for td in time_derivatives) # We support passing in 'transitions', which is a list of both OnEvents # and OnConditions. So, lets filter this by type and add them # appropriately: transitions = normalise_parameter_as_list(transitions) f_dict = filter_discrete_types(transitions, (OnEvent, OnCondition)) self._on_events = [] self._on_conditions = [] # Add all the OnEvents and OnConditions: for event in f_dict[OnEvent]: self.add_on_event(event) for condition in f_dict[OnCondition]: self.add_on_condition(condition) # Sort for equality checking self._on_events = sorted(self._on_events, key=lambda x: x.src_port_name) self._on_conditions = sorted(self._on_conditions, key=lambda x: x.trigger)
def action_componentclass(self, componentclass, namespace): # @UnusedVariable @IgnorePep8 regime_names = [r.name for r in componentclass.regimes] assert_no_duplicates(regime_names)
def action_regime(self, regime, namespace, **kwargs): # @UnusedVariable event_triggers = [on_event.src_port_name for on_event in regime.on_events] assert_no_duplicates(event_triggers)
def __init__(self, name, parameters=None, analog_ports=[], event_ports=[], dynamicsblock=None, subnodes=None, portconnections=None, regimes=None, aliases=None, state_variables=None): """Constructs a DynamicsClass :param name: The name of the componentclass. :param parameters: A list containing either |Parameter| objects or strings representing the parameter names. If ``None``, then the parameters are automatically inferred from the |Dynamics| block. :param analog_ports: A list of |AnalogPorts|, which will be the local |AnalogPorts| for this object. :param event_ports: A list of |EventPorts| objects, which will be the local event-ports for this object. If this is ``None``, then they will be automatically inferred from the dynamics block. :param dynamicsblock: A |DynamicsBlock| object, defining the local dynamicsblock of the componentclass. :param subnodes: A dictionary mapping namespace-names to sub- componentclass. [Type: ``{string:|DynamicsClass|, string:|DynamicsClass|, string:|DynamicsClass|}`` ] describing the namespace of subcomponents for this componentclass. :param portconnections: A list of pairs, specifying the connections between the ports of the subcomponents in this componentclass. These can be `(|NamespaceAddress|, |NamespaceAddress|)' or ``(string, string)``. :param interface: A shorthand way of specifying the **interface** for this componentclass; |Parameters|, |AnalogPorts| and |EventPorts|. ``interface`` takes a list of these objects, and automatically resolves them by type into the correct types. Examples: >>> a = DynamicsClass(name='MyComponent1') .. todo:: Point this towards and example of constructing ComponentClasses. This can't be here, because we also need to know about dynamics. For examples """ # We can specify in the componentclass, and they will get forwarded to # the dynamics class. We check that we do not specify half-and-half: if dynamicsblock is not None: if regimes or aliases or state_variables: err = "Either specify a 'dynamicsblock' parameter, or " err += "state_variables /regimes/aliases, but not both!" raise NineMLRuntimeError(err) else: dynamicsblock = DynamicsBlock(regimes=regimes, aliases=aliases, state_variables=state_variables) ComponentClass.__init__(self, name, parameters, main_block=dynamicsblock) self._query = DynamicsQueryer(self) # Ensure analog_ports is a list not an iterator analog_ports = list(analog_ports) event_ports = list(event_ports) # Check there aren't any duplicates in the port and parameter names assert_no_duplicates(p if isinstance(p, basestring) else p.name for p in chain(parameters if parameters else [], analog_ports, event_ports)) self._analog_send_ports = dict( (p.name, p) for p in analog_ports if isinstance(p, AnalogSendPort)) self._analog_receive_ports = dict((p.name, p) for p in analog_ports if isinstance(p, AnalogReceivePort)) self._analog_reduce_ports = dict((p.name, p) for p in analog_ports if isinstance(p, AnalogReducePort)) # Create dummy event ports to keep the ActionVisitor base class of # the interface inferrer happy self._event_receive_ports = self._event_send_ports = self.subnodes = {} # EventPort, StateVariable and Parameter Inference: inferred_struct = DynamicsClassInterfaceInferer(self) # Check any supplied parameters match: if parameters is not None: inf_check(self._parameters.keys(), inferred_struct.parameter_names, 'Parameters') else: self._parameters = dict( (n, Parameter(n)) for n in inferred_struct.parameter_names) # Check any supplied state_variables match: if self.dynamicsblock._state_variables: state_var_names = [ p.name for p in self.dynamicsblock.state_variables ] inf_check(state_var_names, inferred_struct.state_variable_names, 'StateVariables') else: state_vars = dict((n, StateVariable(n)) for n in inferred_struct.state_variable_names) self.dynamicsblock._state_variables = state_vars # Set and check event receive ports match inferred self._event_receive_ports = dict((p.name, p) for p in event_ports if isinstance(p, EventReceivePort)) if len(self._event_receive_ports): # FIXME: not all OutputEvents are necessarily exposed as Ports, # so really we should just check that all declared output event # ports are in the list of inferred ports, not that the declared # list is identical to the inferred one. inf_check(self._event_receive_ports.keys(), inferred_struct.input_event_port_names, 'Event Ports In') else: # FIXME: TGC don't like this shorthand # Event ports not supplied, so lets use the inferred ones. for pname in inferred_struct.input_event_port_names: self._event_receive_ports[pname] = EventReceivePort(name=pname) # Set and check event send ports match inferred self._event_send_ports = dict( (p.name, p) for p in event_ports if isinstance(p, EventSendPort)) if len(self._event_send_ports): inf_check(self._event_send_ports.keys(), inferred_struct.event_out_port_names, 'Event Ports Out') else: # Event ports not supplied, so lets use the inferred ones. for pname in inferred_struct.event_out_port_names: self._event_send_ports[pname] = EventSendPort(name=pname) # Call namespace mixin constructor _NamespaceMixin.__init__(self, subnodes=subnodes, portconnections=portconnections) # Finalise initiation: self._resolve_transition_regime_names() # Store flattening Information: self._flattener = None # Is the finished componentclass valid?: self._validate_self()
def action_regime(self, regime, namespace, **kwargs): # @UnusedVariable event_triggers = [ on_event.src_port_name for on_event in regime.on_events ] assert_no_duplicates(event_triggers)
def __init__(self, componentclass): PerNamespaceComponentValidator.__init__( self, require_explicit_overrides=True) self.all_objects = list() self.visit(componentclass) assert_no_duplicates(self.all_objects)