def bind(self, container, to_roles=False): """ Binds the PortConnection to the components it is connecting """ # If the sender and receiver should be identified by their role # i.e. pre, pos, response, plasticity, etc... or by name if to_roles: self._sender = getattr(container, self.sender_role) self._receiver = getattr(container, self.receiver_role) else: self._sender = container[self.sender_name] self._receiver = container[self.receiver_name] try: self._send_port = self._sender.send_port(self.send_port_name) except NineMLNameError: raise NineMLNameError( "Could not bind to missing send port, '{}', in '{}'".format( self.send_port_name, self.sender.name)) try: self._receive_port = self._receiver.receive_port( self.receive_port_name) except NineMLNameError: raise NineMLNameError( "Could not bind {} to missing receive port, '{}', in '{}'". format(self, self.receive_port_name, self.receiver.name)) self._check_ports()
def get_attr(self, serial_elem, name, **options): # @UnusedVariable try: value = serial_elem[name] except KeyError: raise NineMLNameError( "Element {} doesn't contain an '{}' attribute".format( serial_elem, name)) except TypeError: raise if self._is_child(value): raise NineMLNameError( "Element {} contains a '{}' child ({}) not an attribute". format(serial_elem, name, value)) return value
def set(self, key, *args): """ Sets the attribute of an annotations "leaf", creating intermediate branches if required Parameters ---------- key : str | tuple(str, str) Name of the next branch in the annotations tree, optionally with a namespace, provided as a tuple (key, namespace). If the namespace is not provided it is taken to be the same as the containing branch *args : list(str) + (int|float|str) A list of subsequent branches to the leaf node followed by the attribute name and a value """ key = self._parse_key(key) # Recurse into branches while there are remaining args key_branches = self._branches[key] if len(key_branches) == 1: branch = key_branches[0] elif not key_branches: name, ns = key branch = _AnnotationsBranch(name, ns, rel_index=len(key_branches)) key_branches.append(branch) else: raise NineMLNameError( "Multiple branches found for key '{}' in annoations branch" " '{}', cannot use 'set' method".format( key, self._name)) branch.set(*args) # recurse into branch
def add(self, key, *args): """ Adds a new branch for the given key. If the key exists already then then an additional branch is appended for that key. Parameters ---------- key : str | tuple(str, str) Name of the next branch in the annotations tree, optionally with a namespace, provided as a tuple (key, namespace). If the namespace is not provided it is taken to be the same as the containing branch *args : list(str) A list of keys for the sub-branches ending in the key of the new sub-branch to add. Intermediate branches that are not present are added implicitly. """ key = self._parse_key(key) key_branches = self._branches[key] if not key_branches or not args: name, ns = key branch = _AnnotationsBranch(name, ns, rel_index=len(key_branches)) key_branches.append(branch) if args: if len(key_branches) > 1: raise NineMLNameError( "Multiple branches found for key '{}' in annoations branch" " '{}', cannot use 'add' method to add a sub-branch" .format(key, self._name)) branch = key_branches[0] branch.add(*args) return branch
def get(self, key, *args, **kwargs): """ Gets the attribute of an annotations "leaf" Parameters ---------- key : str | tuple(str, str) Name of the next branch in the annotations tree, optionally with a namespace, provided as a tuple (key, namespace). If the namespace is not provided it is taken to be the same as the containing branch *args : list(str) + (int|float|str) A list of subsequent branches to the leaf node followed by the attribute name to return the value of default: (int|float|str) The default value to return if the specified annotation has not been set Returns ------- val : (int|float|str) The value of the annotation attribute """ if not args: if 'default' in kwargs: val = self._attr.get(key, kwargs['default']) else: try: val = self._attr[key] except KeyError: raise NineMLNameError( "Annotations branch {{{}}}{} does not contain '{}' " "attribute".format(self.ns, self.name, key)) else: val = super(_AnnotationsBranch, self).get(key, *args, **kwargs) return val
def get_attr(self, serial_elem, name, **options): # @UnusedVariable try: return serial_elem.attrib[name] except KeyError: raise NineMLNameError( "Element {} doesn't contain '{}' attribute".format( serial_elem, name))
def accessor(self, name): try: ports = [population_accessor(p, name) for p in self.populations] except NineMLNameError: raise NineMLNameError( "'{}' {} is not present in all populations '{}' of the " "selection" .format(name, population_accessor.__name__, "', '".join(p.name for p in self.populations))) port = ports[0] if any(p != port for p in ports): raise NineMLNameError( "{} '{}' in populations '{}' are not equivalent" .format(population_accessor.__name__.capitalize(), name, "', '".join(p.name for p in self.populations))) return port
def delete(self, key, *args, **kwargs): """ Gets the attribute of an annotations "leaf" Parameters ---------- key : str | tuple(str, str) Name of the next branch in the annotations tree, optionally with a namespace, provided as a tuple (key, namespace). If the namespace is not provided it is taken to be the same as the containing branch *args : list(str) + (int|float|str) A list of subsequent branches to the leaf node followed by the attribute name to delete """ key = self._parse_key(key) if key in self._branches: key_branches = self._branches[key] if len(key_branches) == 1: # Recurse into branches while there are remaining args key_branches[0].delete(*args, **kwargs) if key_branches[0].empty(): del self._branches[key] else: raise NineMLNameError( "Multiple branches found for key '{}' in annoations " "branch '{}', cannot use 'delete' method".format( key, self._name))
def property(self, name): try: return self._properties[name] except KeyError: try: return self.definition.component.property(name) except AttributeError: raise NineMLNameError( "No property named '{}' in component class".format(name))
def on_condition(self, condition): if not isinstance(condition, sympy.Basic): condition = Trigger(condition).rhs try: local_on_condition = next(oc for oc in self.on_conditions if oc.trigger.rhs == condition) except StopIteration: raise NineMLNameError(condition) return _NamespaceOnCondition(self.sub_component, local_on_condition, self)
def send_port(self, name): try: return self.event_send_port(name) except NineMLNameError: try: return self.analog_send_port(name) except NineMLNameError: raise NineMLNameError( "'{}' Dynamics object does not have a send port " "named '{}'".format(self.name, name))
def port(self, name): try: return self.send_port(name) except NineMLNameError: try: return self.receive_port(name) except NineMLNameError: raise NineMLNameError( "'{}' Dynamics object does not have a port named '{}'". format(self.name, name))
def initial_value(self, name): try: return self._initial_values[name] except KeyError: try: return self.definition.component.initial_value(name) except AttributeError: raise NineMLNameError( "No initial value named '{}' in component class".format( name))
def get(self, key, *args, **kwargs): """ Gets the attribute of an annotations "leaf" Parameters ---------- key : str | tuple(str, str) Name of the next branch in the annotations tree, optionally with a namespace, provided as a tuple (key, namespace). If the namespace is not provided it is taken to be the same as the containing branch *args : list(str) + (int|float|str) A list of subsequent branches to the leaf node followed by the attribute name to return the value of default: (int|float|str) The default value to return if the specified annotation has not been set Returns ------- val : (int|float|str) The value of the annotation attribute """ key = self._parse_key(key) if key in self._branches: key_branches = self._branches[key] if len(key_branches) == 1: # Recurse into branches while there are remaining args val = key_branches[0].get(*args, **kwargs) else: raise NineMLNameError( "Multiple branches found for key '{}' in annoations " "branch '{}', cannot use 'get' method".format( key, self._name)) else: if 'default' in kwargs: return kwargs['default'] else: raise NineMLNameError( "No annotation at path '{}'".format("', '".join(args))) return val
def branch(self, key_index): try: name, ns, index = key_index except: raise try: return self._branches[(name, ns)][index] except (KeyError, IndexError): raise NineMLNameError( "{} branch not present in annotations ({})" .format('{}-{}-{}'.format(*key_index), ", ".join('{}-{}-{}'.format(*k) for k in self.branch_keys)))
def receive_port(self, name): try: return self.event_receive_port(name) except NineMLNameError: try: return self.analog_receive_port(name) except NineMLNameError: try: return self.analog_reduce_port(name) except NineMLNameError: raise NineMLNameError( "'{}' Dynamics object does not have a receive port " "named '{}'".format(self.name, name))
def split_namespace(identifier_in_namespace): """ Splits an identifer and a namespace that have been concatenated by 'append_namespace' """ parts = double_underscore_re.split(identifier_in_namespace) if len(parts) < 2: raise NineMLNameError( "Identifier '{}' does not belong to a sub-namespace".format( identifier_in_namespace)) name = '__'.join(parts[:-1]) comp_name = parts[-1] comp_name = more_than_double_underscore_re.sub(r'\1', comp_name) return name, comp_name
def find_element(self, sym): name = Expression.symbol_to_str(sym) element = None for context in reversed(self.contexts): try: element = context.parent.element( name, child_types=context.parent_cls.nineml_children) except KeyError: pass if element is None: raise NineMLNameError( "'{}' element was not found in component class '{}'" .format(sym, self.component_class.name)) return element
def add(self, nineml_obj, clone=True, cloner=None, **kwargs): """ Adds a cloned version of the element to the document, setting the document reference (and the corresponding url) of clones to the document. Parameters ---------- nineml_obj : DocumentLevelObject A document level object to add to the document clone : bool Whether to clone the element before adding it to the document clone_definitions : str Whether to clone definitions of user layer objects kwargs : dict Keyword arguments passed to the clone method """ if not isinstance(nineml_obj, DocumentLevelObject): raise NineMLUsageError( "Cannot add {} element to document as it is not a \"document" "-level\" object".format(nineml_obj)) if nineml_obj.name in self: # Ignore if the element is already added (this can happen # implictly when writing other elements that refer to this element) if nineml_obj is not self[nineml_obj.name]: if nineml_obj == self[nineml_obj.name]: nineml_obj = self[nineml_obj.name] else: nineml_obj.find_mismatch(self[nineml_obj.name]) raise NineMLNameError( "Could not add '{}' as a different object with that " "name already exists in the document '{}':\n{}" .format(nineml_obj.name, self.url, nineml_obj.find_mismatch( self[nineml_obj.name]))) elif nineml_obj.document is not None and not clone: raise NineMLUsageError( "Attempting to add the same object '{}' {} to document" " '{}' document when it is already in another " "document, '{}' and 'clone' kwarg is False" .format(nineml_obj.name, nineml_obj.nineml_type, self.url, nineml_obj.document.url)) else: if clone: if cloner is None: cloner = Cloner(**kwargs) nineml_obj = cloner.clone(nineml_obj, **kwargs) AddToDocumentVisitor(self).visit(nineml_obj, **kwargs) return nineml_obj
def remove(self, nineml_obj, ignore_missing=False): if not isinstance(nineml_obj, DocumentLevelObject): raise NineMLUsageError( "Could not remove {} from document as it is not a document " "level NineML object ('{}') ".format(nineml_obj.key, nineml_obj.nineml_type)) try: del self[nineml_obj.name] except KeyError: if not ignore_missing: raise NineMLNameError( "Could not find '{}' element to remove from document '{}'" .format(nineml_obj.name, self.url)) assert nineml_obj.document is self nineml_obj._document = None
def __getitem__(self, key): """ Returns the list of sub-branches for the given key Parameters ---------- key : str The name of the annotations branch(es) to return """ key = self._parse_key(key) if key in self._branches: key_branches = self._branches[key] else: raise NineMLNameError( "{} annotations branch does not have branch or attribute '{}'" .format(self._name, key)) return key_branches
def set(self, prop): try: super(DynamicsProperties, self).set(prop) except NineMLNameError: try: state_variable = self.component_class.state_variable(prop.name) except NineMLNameError: raise NineMLNameError( "'{}' Dynamics does not have a Parameter or StateVariable " "named '{}'".format(self.component_class.name, prop.name)) if prop.units.dimension != state_variable.dimension: raise NineMLUnitMismatchError( "Dimensions for '{}' initial value ('{}') don't match that" " of state variable in component class ('{}').".format( prop.name, prop.units.dimension.name, state_variable.dimension.name)) self._initial_values[prop.name] = prop
def synapse(self, name): try: synapses = set( ca.nineml.dynamics_properties.synapse_properties(name) for ca in self.component_arrays) except NineMLNameError: raise NineMLNameError( "Could not return synapse '{}' because it is missing from " "one or more of the component arrays in '{}' Selection" .format(name, self.name)) if len(synapses) > 1: raise Pype9RuntimeError( "'{}' varies ({}) between component arrays in '{}' Selection" .format(name, ', '.join(str(s) for s in synapses), self.name)) try: return next(iter(synapses)) # Return the only synapse except: raise
def element(self, name, child_types=None, include_send_ports=False): """ Looks a member item by "name" (identifying characteristic) Parameters ---------- name : str Name of the element to return nineml_children : dict[str, str] Mapping from element type to accessor name include_send_ports: As send ports will typically mask the name as an alias or state variable (although not necessarily in MultiDynamics objects) they are ignored unless this kwarg is set to True, in which case they will be returned only if no state variable or alias is found. Returns ------- elem : NineMLBaseObject The element corresponding to the provided 'name' argument """ if child_types is None: child_types = self.nineml_children send_port = None for child_type in child_types: try: elem = self._member_accessor(child_type)(name) # Ignore send ports as they otherwise mask # aliases/state variables if isinstance(elem, SendPortBase): send_port = elem else: return elem # No need to wait to end of loop except NineMLNameError: pass if include_send_ports and send_port is not None: return send_port else: raise NineMLNameError( "'{}' was not found in '{}' {} object".format( name, self.key, self.__class__.__name__))
def __getitem__(self, name): """ Returns the element referenced by the given name """ if name is None: # FIXME: This is a hack that should be removed # This simplifies code in a few places where an optional # attribute refers to a name of an object which # should be resolved if present but be set to None if not. return None try: nineml_obj = super(Document, self).__getitem__(name) except KeyError: if self._unserializer is not None: nineml_obj = self._unserializer.load_element(name) else: raise NineMLNameError( "'{}' was not found in the NineML document {} (elements in" " the document were '{}').".format( name, self.url or '', "', '".join(iter(self.keys())))) return nineml_obj
def delete(self, key, *args, **kwargs): """ Gets the attribute of an annotations "leaf" Parameters ---------- key : str | tuple(str, str) Name of the next branch in the annotations tree, optionally with a namespace, provided as a tuple (key, namespace). If the namespace is not provided it is taken to be the same as the containing branch *args : list(str) + (int|float|str) A list of subsequent branches to the leaf node followed by the attribute name to delete """ if not args: try: del self.attr[key] except KeyError: raise NineMLNameError( "Annotations branch {{{}}}{} does not contain '{}' " "attribute".format(self.ns, self.name, key)) else: super(_AnnotationsBranch, self).delete(key, *args, **kwargs)
def load_element(self, name, **options): """ Lazily loads the document-level object named and all elements it references Parameter --------- name : str Name of the document level object to load """ try: serial_elem, nineml_cls = self._doc_elems[name] except KeyError: raise NineMLNameError( "'{}' was not found in the NineML document {} (elements in " "the document were '{}').".format( name, self.url or '', "', '".join(iter(self._doc_elems.keys())))) nineml_object = self.visit(serial_elem, nineml_cls, **options) AddToDocumentVisitor(self.document, **options).visit(nineml_object, **options) self._loaded_elems.append(name) return nineml_object
def record(self, port_name, interval=None, **kwargs): # @UnusedVariable @IgnorePep8 # Create dictionaries for storing local recordings. These are not # created initially to save memory if recordings are not required or # handled externally self._initialize_local_recording() try: port = self.component_class.send_port(port_name) except NineMLNameError: try: # For convenient access to state variables port = self.component_class.state_variable(port_name) except NameError: raise NineMLNameError( "No matching state variable or event send port matching " "port name '{}' in component class '{}'".format( port_name, self.component_class.name)) if port.nineml_type in ('EventSendPort', 'EventSendPortExposure'): # FIXME: This assumes that all event send port are spikes, which # I think is currently a limitation of NEST self._recorders[port_name] = recorder = nest.Create( "spike_detector", params={"precise_times": True}) nest.Connect(self._cell, recorder) else: if interval is None: interval = Simulation.active().dt interval = float(interval.in_units(un.ms)) variable_name = self.build_name(port_name) self._recorders[port_name] = recorder = nest.Create( 'multimeter', 1, {"interval": interval}) nest.SetStatus(recorder, {'record_from': [variable_name]}) nest.Connect(recorder, self._cell, syn_spec={'delay': self.device_delay_ms})
def split_multi_regime_name(name): parts = triple_underscore_re.split(name) if not parts: raise NineMLNameError("'{}' is not a multi-regime name".format(name)) return tuple(more_than_double_underscore_re.sub(r'\1', p) for p in parts)