def __init__(self, nineml_model, build_mode='lazy', **kwargs): if not isinstance(nineml_model, ComponentArray9ML): raise Pype9RuntimeError( "Expected a component array, found {}".format(nineml_model)) self._nineml = nineml_model dynamics_properties = nineml_model.dynamics_properties dynamics = dynamics_properties.component_class celltype = self.PyNNCellWrapperMetaClass( component_class=dynamics, default_properties=dynamics_properties, initial_state=list(dynamics_properties.initial_values), initial_regime=dynamics_properties.initial_regime, build_mode=build_mode, **kwargs) if build_mode != 'build_only': rng = self.Simulation.active().properties_rng cellparams = dict( (p.name, get_pyNN_value(p, self.UnitHandler, rng)) for p in dynamics_properties.properties) initial_values = dict( (i.name, get_pyNN_value(i, self.UnitHandler, rng)) for i in dynamics_properties.initial_values) initial_values['_regime'] = celltype.model.regime_index( dynamics_properties.initial_regime) # NB: Simulator-specific derived classes extend the corresponding # PyNN population class self.PyNNPopulationClass.__init__( self, nineml_model.size, celltype, cellparams=cellparams, initial_values=initial_values, label=nineml_model.name) self._inputs = {} self._t_stop = None self.Simulation.active().register_array(self)
def connections(self): if not self.has_been_sampled(): raise Pype9RuntimeError( "Connections have not been generated for PyNNConnectivity " "object (they are only generated during network construction " "for efficiency") raise NotImplementedError( "Need to work out connection list from connection map")
def __init__(self, nineml_model, build_mode='lazy', **kwargs): if isinstance(nineml_model, basestring): nineml_model = nineml.read(nineml_model).as_network( name=os.path.splitext(os.path.basename(nineml_model))[0]) elif isinstance(nineml_model, Document): if nineml_model.url is not None: name = os.path.splitext(os.path.basename(nineml_model.url))[0] else: name = "Anonymous" nineml_model = nineml_model.as_network(name=name) self._nineml = nineml_model.clone() # Get RNG for random distribution values and connectivity rng = self.Simulation.active().properties_rng if build_mode != 'build_only': self.nineml.resample_connectivity( connectivity_class=self.ConnectivityClass, rng=rng) (flat_comp_arrays, flat_conn_groups, flat_selections) = self._flatten_to_arrays_and_conns(self._nineml) self._component_arrays = {} # Build the PyNN populations # Add build args to distinguish models built for this network as # opposed to other networks build_url = kwargs.pop('build_url', nineml_model.url) build_version = nineml_model.name + kwargs.pop('build_version', '') for name, comp_array in flat_comp_arrays.items(): self._component_arrays[name] = self.ComponentArrayClass( comp_array, build_mode=build_mode, build_url=build_url, build_version=build_version, **kwargs) self._selections = {} # Build the PyNN Selections for selection in flat_selections.values(): # TODO: Assumes that selections are only concatenations (which is # true for 9MLv1.0 but not v2.0) self._selections[selection.name] = self.SelectionClass( selection, *[self.component_array(p.name) for p in selection.populations]) if build_mode != 'build_only': # Set the connectivity objects of the projections to the # PyNNConnectivity class if self.nineml.connectivity_has_been_sampled(): raise Pype9RuntimeError( "Connections have already been sampled, please reset them" " using 'resample_connectivity' before constructing " "network") self._connection_groups = {} for name, conn_group in flat_conn_groups.items(): try: source = self._component_arrays[conn_group.source.name] except KeyError: source = self._selections[conn_group.source.name] try: destination = self._component_arrays[ conn_group.destination.name] except KeyError: destination = self._selections[conn_group.destination.name] self._connection_groups[name] = self.ConnectionGroupClass( conn_group, source=source, destination=destination) self._finalise_construction()
def path_to_utility(self, utility_name, env_var='', **kwargs): # @UnusedVariable @IgnorePep8 """ Returns the full path to an executable by searching the "PATH" environment variable Parameters ---------- utility_name : str Name of executable to search the execution path env_var : str Name of a environment variable to lookup first before searching path default : str | None The default value to assign to the path if it cannot be found. Returns ------- utility_path : str Full path to executable """ if kwargs and list(kwargs) != ['default']: raise Pype9RuntimeError( "Should only provide 'default' as kwarg to path_to_utility " "provided ({})".format(kwargs)) try: utility_path = os.environ[env_var] except KeyError: if platform.system() == 'Windows': utility_name += '.exe' # Get the system path system_path = os.environ['PATH'].split(os.pathsep) # Append NEST_INSTALL_DIR/NRNHOME if present system_path.extend(self.simulator_specific_paths()) # Check the system path for the command utility_path = None for dr in system_path: path = join(dr, utility_name) if os.path.exists(path): utility_path = path break if not utility_path: try: utility_path = kwargs['default'] except KeyError: raise Pype9CommandNotFoundError( "Could not find executable '{}' on the system path " "'{}'".format(utility_name, ':'.join(system_path))) else: if not os.path.exists(utility_path): raise Pype9CommandNotFoundError( "Could not find executable '{}' at path '{}' provided by " "'{}' environment variable".format(utility_name, env_var)) return utility_path
def _check_connection_properties(self, port_name, properties): props_dict = dict((p.name, p) for p in properties) try: param_set = self._nineml.component_class.connection_parameter_set( port_name) except NineMLNameError: return # No parameter set, so no need to check params_dict = dict((p.name, p) for p in param_set.parameters) if set(props_dict.keys()) != set(params_dict.keys()): raise Pype9RuntimeError( "Mismatch between provided property and parameter names:" "\nParameters: '{}'\nProperties: '{}'" .format("', '".join(iter(params_dict.keys())), "', '".join(iter(props_dict.keys())))) for prop in properties: if params_dict[prop.name].dimension != prop.units.dimension: raise Pype9RuntimeError( "Dimension of property '{}' ({}) does not match that of " "the corresponding parameter ({})" .format(prop.name, prop.units.dimension, params_dict[prop.name].dimension))
def wrap(cls, dynamics_properties, synapses_properties=[], connection_property_sets=[]): if isinstance(dynamics_properties, MultiDynamicsProperties): wrapped_cls = MultiDynamicsWithSynapsesProperties elif isinstance(dynamics_properties, DynamicsProperties): wrapped_cls = DynamicsWithSynapsesProperties else: raise Pype9RuntimeError( "Cannot wrap '{}' class with WithSynapses, only Dynamics and " "MultiDynamics".format(type(dynamics_properties))) return wrapped_cls(dynamics_properties.name, dynamics_properties, synapses_properties, connection_property_sets)
def __init__(self, name, dynamics, synapses, connection_parameter_sets): assert isinstance(dynamics, (Dynamics, MultiDynamics)) # Initialise Dynamics/MultiDynamics base classes self._name = validate_identifier(name) self._dynamics = dynamics self.add(*synapses) self.add(*connection_parameter_sets) for conn_param in self.all_connection_parameters(): try: dyn_param = self._dynamics.parameter(conn_param.name) if conn_param.dimension != dyn_param.dimension: raise Pype9RuntimeError( "Inconsistent dimensions between connection parameter" " '{}' ({}) and parameter of the same name ({})". format(conn_param.name, conn_param.dimension, dyn_param.dimension)) except NineMLNameError: raise Pype9RuntimeError( "Connection parameter '{}' does not refer to a parameter " "in the base MultiDynamics class ('{}')".format( conn_param, "', '".join(sp.name for sp in self._dynamics.parameters))) self._dimension_resolver = None
def wrap(cls, dynamics, synapses=None, connection_parameter_sets=None): if synapses is None: synapses = [] if connection_parameter_sets is None: connection_parameter_sets = [] if isinstance(dynamics, MultiDynamics): wrapped_cls = MultiDynamicsWithSynapses elif isinstance(dynamics, Dynamics): wrapped_cls = DynamicsWithSynapses else: raise Pype9RuntimeError( "Cannot wrap '{}' class with WithSynapses, only Dynamics and " "MultiDynamics".format(type(dynamics))) name = dynamics.name dynamics = dynamics.clone() dynamics.name = name + '__sans_synapses' return wrapped_cls(name, dynamics, synapses, connection_parameter_sets)
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 __init__(self, nineml_model, source, destination): rng = self.Simulation.active().properties_rng if not isinstance(nineml_model, EventConnectionGroup9ML): raise Pype9RuntimeError( "Expected a connection group model, found {}" .format(nineml_model)) try: (synapse, conns) = destination.synapse(nineml_model.name) if conns is not None: raise NotImplementedError( "Nonlinear synapses, as used in '{}' are not currently " "supported".format(nineml_model.name)) if synapse.num_local_properties == 1: # Get the only local property that varies with the synapse # (typically the synaptic weight but does not have to be) weight = get_pyNN_value(next(synapse.local_properties), self.UnitHandler, rng) elif not synapse.num_local_properties: weight = 0.0 else: raise NotImplementedError( "Currently only supports one property that varies with " "each synapse") except NineMLNameError: # FIXME: Should refactor "WithSynapses" code to "CellAndSynapses" # class which inherits most of its functionality from # MultiDynamics to ensure that every connection has a # "synapse" even if it is just a simple port exposure # Synapse dynamics properties didn't have any properties that vary # between synapses so wasn't included weight = 0.0 self._nineml = nineml_model delay = get_pyNN_value(nineml_model.delay, self.UnitHandler, rng) # FIXME: Ignores send_port, assumes there is only one... # NB: Simulator-specific derived classes extend the corresponding # PyNN population class self.PyNNProjectionClass.__init__( self, presynaptic_population=source, postsynaptic_population=destination, connector=nineml_model.connectivity, synapse_type=self.SynapseClass(weight=weight, delay=delay), receptor_type=nineml_model.destination_port, label=nineml_model.name)
def __setattr__(self, varname, val): # Capture attributes ending with '_init' (the convention PyNN uses to # specify initial conditions of state variables) and save them in # initial states if varname == '_regime_init': object.__setattr__(self, '_regime_index', val) elif (varname.endswith('_init') and varname[:-5] in self.component_class.state_variable_names): if varname in chain(self.component_class.state_variable_names, self.component_class.parameter_names): raise Pype9RuntimeError( "Ambiguous variable '{}' can either be the initial state " "of '{}' or a parameter/state-variable".format( varname, varname[:-5])) self._initial_states[varname[:-5]] = val elif varname in ('record_times', 'recording_time'): # PyNN needs to be able to set 'record_times' and 'recording_time' # to record state variables object.__setattr__(self, varname, val) else: super(Cell, self).__setattr__(varname, val)
def from_pq_quantity(cls, qty): if isinstance(qty, Quantity): return qty # If already a 9ML quantity elif isinstance(qty, (int, float)): units = un.unitless elif isinstance(qty, pq.Quantity): unit_name = str(qty.units).split()[1].replace( '/', '_per_').replace('**', '').replace('*', '_').replace( '(', '').replace(')', '') if unit_name.startswith('_per_'): unit_name = unit_name[1:] # strip leading underscore powers = dict( (cls._pq_si_to_dim[type(u)], p) for u, p in qty.units.simplified._dimensionality.items()) dimension = un.Dimension(unit_name + 'Dimension', **powers) units = un.Unit(unit_name, dimension=dimension, power=int(log10(float(qty.units.simplified)))) else: raise Pype9RuntimeError( "Cannot '{}' to nineml.Quantity (can only convert " "quantities.Quantity and numeric objects)" .format(qty)) return Quantity(float(qty), units)
def _flatten_to_arrays_and_conns(cls, network_model): """ Convert populations and projections into component arrays and connection groups """ component_arrays = {} connection_groups = {} # Create flattened component with all synapses combined with the post- # synaptic cell dynamics using MultiDynamics for pop in network_model.populations: # Get all the projections that project to/from the given population receiving = [p for p in network_model.projections if (pop == p.post or (p.post.nineml_type == 'Selection' and pop in p.post.populations))] sending = [p for p in network_model.projections if (pop == p.pre or (p.pre.nineml_type == 'Selection' and pop in p.pre.populations))] # Create a dictionary to hold the cell dynamics and any synapse # dynamics that can be flattened into the cell dynamics # (i.e. linear ones). sub_components = {cls.CELL_COMP_NAME: pop.cell} # All port connections between post-synaptic cell and linear # synapses and port exposures to pre-synaptic cell internal_conns = [] exposures = [] def add_exposures(exposures_to_add): """ Adds exposures to a "set" of exposures. If 9ML objects were hashable could use a 'set'. """ for pe in exposures_to_add: if pe not in exposures: exposures.append(pe) synapses = [] connection_property_sets = [] # FIXME: There has to be a way of avoiding this name clash if any(p.name == cls.CELL_COMP_NAME for p in receiving): raise Pype9RuntimeError( "Cannot handle projections named '{}' (why would you " "choose such a silly name?;)".format(cls.CELL_COMP_NAME)) for proj in receiving: # Flatten response and plasticity into single dynamics class. # TODO: this should be no longer necessary when we move to # version 2 as response and plasticity elements will be # replaced by a synapse element in the standard. It will need # be copied at this point though as it is modified synapse, proj_conns = cls._flatten_synapse(proj) # Get all connections to/from the pre-synaptic cell pre_conns = [pc for pc in proj_conns if 'pre' in (pc.receiver_role, pc.sender_role)] # Get all connections between the synapse and the post-synaptic # cell post_conns = [pc for pc in proj_conns if pc not in pre_conns] # Mapping of port connection role to sub-component name role2name = {'post': cls.CELL_COMP_NAME} # If the synapse is non-linear it can be combined into the # dynamics of the post-synaptic cell. try: if not synapse.component_class.is_linear(): raise Pype9UnflattenableSynapseException() role2name['synapse'] = proj.name # Extract "connection weights" (any non-singular property # value) from the synapse properties connection_property_sets.extend( cls._extract_connection_property_sets(synapse, proj.name)) # Add the flattened synapse to the multi-dynamics sub # components sub_components[proj.name] = synapse.clone() # Convert port connections between synpase and post- # synaptic cell into internal port connections of a multi- # dynamics object internal_conns.extend(pc.assign_names_from_roles(role2name) for pc in post_conns) # Expose ports that are needed for the pre-synaptic # connections except Pype9UnflattenableSynapseException: # All synapses (of this type) connected to a single post- # synaptic cell cannot be flattened into a single component # of a multi- dynamics object so an individual synapses # must be created for each connection. synapse_conns = [ pc.append_namespace_from_roles( {'post': cls.CELL_COMP_NAME, 'pre': cls.CELL_COMP_NAME, 'synapse': proj.name}) for pc in post_conns] synapses.append(SynapseProperties(proj.name, synapse, synapse_conns)) # Add exposures to the post-synaptic cell for connections # from the synapse add_exposures(chain(*( pc.expose_ports({'post': cls.CELL_COMP_NAME}) for pc in post_conns))) # Add exposures for connections to/from the pre synaptic cell add_exposures( chain(*(pc.expose_ports(role2name) for pc in pre_conns))) role2name['pre'] = cls.CELL_COMP_NAME # Add exposures for connections to/from the pre-synaptic cell in # populations. for proj in sending: # Not required after transition to version 2 syntax synapse, proj_conns = cls._flatten_synapse(proj) # Add send and receive exposures to list add_exposures(chain(*( pc.expose_ports({'pre': cls.CELL_COMP_NAME}) for pc in proj_conns))) # Add all cell ports as multi-component exposures that aren't # connected internally in case the user would like to save them or # play data into them internal_cell_ports = set(chain( (pc.send_port_name for pc in internal_conns if pc.sender_name == cls.CELL_COMP_NAME), (pc.receive_port_name for pc in internal_conns if pc.receiver_name == cls.CELL_COMP_NAME))) add_exposures( BasePortExposure.from_port(p, cls.CELL_COMP_NAME) for p in pop.cell.ports if p.name not in internal_cell_ports) dynamics_properties = MultiDynamicsProperties( name=pop.name + '_cell', sub_components=sub_components, port_connections=internal_conns, port_exposures=exposures) component = MultiDynamicsWithSynapsesProperties( dynamics_properties.name, dynamics_properties, synapse_propertiess=synapses, connection_property_sets=connection_property_sets) array_name = pop.name component_arrays[array_name] = ComponentArray9ML( array_name, pop.size, component) selections = {} for sel in network_model.selections: selections[sel.name] = Selection9ML( sel.name, Concatenate9ML(component_arrays[p.name] for p in sel.populations)) arrays_and_selections = dict( chain(iter(component_arrays.items()), iter(selections.items()))) # Create ConnectionGroups from each port connection in Projection for proj in network_model.projections: _, proj_conns = cls._flatten_synapse(proj) # Get all connections to/from the pre-synaptic cell pre_conns = [pc for pc in proj_conns if 'pre' in (pc.receiver_role, pc.sender_role)] # Create a connection group for each port connection of the # projection to/from the pre-synaptic cell for port_conn in pre_conns: ConnectionGroupClass = ( EventConnectionGroup9ML if port_conn.communicates == 'event' else AnalogConnectionGroup9ML) if len(pre_conns) > 1: name = ('__'.join((proj.name, port_conn.sender_role, port_conn.send_port_name, port_conn.receiver_role, port_conn.receive_port_name))) else: name = proj.name if port_conn.sender_role == 'pre': connectivity = proj.connectivity # If a connection from the pre-synaptic cell the delay # is included # TODO: In version 2 all port-connections will have # their own delays delay = proj.delay else: # If a "reverse connection" to the pre-synaptic cell # the connectivity needs to be inverted connectivity = InversePyNNConnectivity( proj.connectivity) delay = 0.0 * un.s # Append sub-component namespaces to the source/receive # ports ns_port_conn = port_conn.append_namespace_from_roles( {'post': cls.CELL_COMP_NAME, 'pre': cls.CELL_COMP_NAME, 'synapse': proj.name}) conn_group = ConnectionGroupClass( name, arrays_and_selections[proj.pre.name], arrays_and_selections[proj.post.name], source_port=ns_port_conn.send_port_name, destination_port=(ns_port_conn.receive_port_name), connectivity=connectivity, delay=delay) connection_groups[conn_group.name] = conn_group return component_arrays, connection_groups, selections
""" This package mirrors the one in pyNN Author: Thomas G. Close ([email protected]) Copyright: 2012-2014 Thomas G. Close. License: This file is part of the "NineLine" package, which is released under the MIT Licence, see LICENSE for details. """ from __future__ import absolute_import import sys from pype9.exceptions import Pype9RuntimeError # Remove any system arguments that may conflict with if '--debug' in sys.argv: raise Pype9RuntimeError( "'--debug' argument passed to script conflicts with an argument to " "nest, causing the import to stop at the NEST prompt") import pyNN.nest # @IgnorePep8 from pyNN.common.control import build_state_queries # @IgnorePep8 from pyNN.nest.standardmodels.synapses import StaticSynapse # @IgnorePep8 from pype9.simulate.common.network.base import ( # @IgnorePep8 Network as BaseNetwork, ComponentArray as BaseComponentArray, ConnectionGroup as BaseConnectionGroup, Selection as BaseSelection) import pyNN.nest.simulator as simulator # @IgnorePep8 from .cell_wrapper import PyNNCellWrapperMetaClass # @IgnorePep8 from .connectivity import PyNNConnectivity # @IgnorePep8 from ..code_gen import CodeGenerator as CodeGenerator # @IgnorePep8 from ..units import UnitHandler # @IgnorePep8 from ..simulation import Simulation # @IgnorePep8 (get_current_time, get_time_step, get_min_delay, get_max_delay, num_processes,
def play(self, port_name, signal, properties=[]): """ Plays an analog signal or train of events into a port of the dynamics array. Parameters ---------- port_name : str The name of the port to play the signal into signal : neo.AnalogSignal | neo.SpikeTrain The signal to play into the cell properties : dict(str, nineml.Quantity) Connection properties when playing into a event receive port with static connection properties """ port = self.celltype.model.component_class.receive_port(port_name) if port.nineml_type in ('EventReceivePort', 'EventReceivePortExposure'): # Shift the signal times to account for the minimum delay and # match the NEURON implementation try: spike_trains = Sequence(signal.rescale(pq.ms) - self._min_delay * pq.ms) source_size = 1 except ValueError: # Assume multiple signals spike_trains = [] for spike_train in signal: spike_train = (spike_train.rescale(pq.ms) - self._min_delay * pq.ms) if any(spike_train <= 0.0): raise Pype9RuntimeError( "Some spike times are less than device delay ({}) " "and so can't be played into cell ({})".format( self._min_delay, ', '.join(str(st) for st in spike_train[ spike_train < self._min_delay]))) spike_trains.append(Sequence(spike_train)) source_size = len(spike_trains) input_pop = self.PyNNPopulationClass( source_size, self.SpikeSourceArray, cellparams={'spike_times': spike_trains}, label='{}-{}-input'.format(self.name, port_name)) # self.celltype.model()._check_connection_properties(port_name, # properties) if len(properties) > 1: raise NotImplementedError( "Cannot handle more than one connection property per port") elif properties: weight = self.UnitHandler.scale_value(properties[0].quantity) else: weight = 1.0 # The weight var is not used connector = (self.OneToOneConnector() if source_size > 1 else self.AllToAllConnector()) input_proj = self.PyNNProjectionClass( input_pop, self, connector, self.SynapseClass(weight=weight, delay=self._min_delay), receptor_type=port_name, label='{}-{}-input_projection'.format(self.name, port_name)) self._inputs[port_name] = (input_pop, input_proj) elif port.nineml_type in ('AnalogReceivePort', 'AnalogReducePort', 'AnalogReceivePortExposure', 'AnalogReducePortExposure'): raise NotImplementedError # # Signals are played into NEST cells include a delay (set to be the # # minimum), which is is subtracted from the start of the signal so # # that the effect of the signal aligns with other simulators # self._inputs[port_name] = nest.Create( # 'step_current_generator', 1, # {'amplitude_values': pq.Quantity(signal, 'pA'), # 'amplitude_times': ( # signal.times.rescale(pq.ms) - # controller.device_delay * pq.ms), # 'start': float(signal.t_start.rescale(pq.ms)), # 'stop': float(signal.t_stop.rescale(pq.ms))}) # nest.Connect(self._inputs[port_name], self._cell, # syn_spec={ # "receptor_type": self._receive_ports[port_name], # 'delay': controller.device_delay}) else: raise Pype9RuntimeError( "Unrecognised port type '{}' to play signal into".format(port))
def _create_NEST(self, nest_name): trans_params = {} for prop in self.properties.properties: name = prop.name value = prop.value try: varname, scale = self.nest_translations[name] value = value * scale except (ValueError, KeyError): varname = self.nest_translations.get(name, name) value = UnitHandlerNEST.scale_value(Quantity(value, prop.units)) if varname is not None: trans_params[varname] = value self.nest_cell = nest.Create(nest_name, 1, trans_params) try: receptor_types = nest.GetDefaults(nest_name)['receptor_types'] except KeyError: receptor_types = None if self.input_signal is not None: port_name, signal = self.input_signal generator = nest.Create( 'step_current_generator', 1, { 'amplitude_values': numpy.ravel(pq.Quantity(signal, 'pA')), 'amplitude_times': numpy.ravel(numpy.asarray(signal.times.rescale(pq.ms))) - self.device_delay, 'start': float(signal.t_start.rescale(pq.ms)), 'stop': float(signal.t_stop.rescale(pq.ms)) }) nest.Connect( generator, self.nest_cell, syn_spec={ 'receptor_type': (receptor_types[port_name] if receptor_types else 0), 'delay': self.device_delay }) if self.input_train is not None: port_name, signal, connection_properties = self.input_train try: _, scale = self.nest_translations[port_name] except KeyError: scale = 1.0 # FIXME: Should scale units weight = connection_properties[0].value * scale spike_times = (numpy.asarray(signal.rescale(pq.ms)) - self.device_delay) if any(spike_times < 0.0): raise Pype9RuntimeError( "Some spike are less than minimum delay and so can't be " "played into cell ({})".format(', '.join( str(t) for t in spike_times[ spike_times < self.device_delay]))) generator = nest.Create('spike_generator', 1, {'spike_times': spike_times}) nest.Connect( generator, self.nest_cell, syn_spec={ 'receptor_type': (receptor_types[port_name] if receptor_types else 0), 'delay': self.device_delay, 'weight': float(weight) }) self.nest_multimeter = nest.Create( 'multimeter', 1, {"interval": self.to_float(self.dt, 'ms')}) nest.SetStatus(self.nest_multimeter, {'record_from': [self.nest_state_variable]}) nest.Connect(self.nest_multimeter, self.nest_cell, syn_spec={'delay': self.device_delay}) trans_states = {} for name, qty in self.initial_states.items(): try: varname, scale = self.nest_translations[name] qty = qty * scale except (ValueError, KeyError): varname = self.nest_translations.get(name, name) value = UnitHandlerNEST.scale_value(qty) if varname is not None: trans_states[varname] = value nest.SetStatus(self.nest_cell, trans_states)
def __init__(self, nineml_model=None, properties=None, initial_states=None, initial_regime=None, state_variable='v', dt=0.01, simulators=None, neuron_ref=None, nest_ref=None, input_signal=None, input_train=None, neuron_translations=None, nest_translations=None, neuron_build_args=None, nest_build_args=None, min_delay=0.1, device_delay=0.1, max_delay=10.0, extra_mechanisms=None, extra_point_process=None, auxiliary_states=None): if nineml_model is not None and not simulators: raise Pype9RuntimeError( "No simulators specified to simulate the 9ML model '{}'." "Add either 'neuron', 'nest' or both to the positional " "arguments".format(nineml_model.name)) self.simulate_neuron = 'neuron' in simulators self.simulate_nest = 'nest' in simulators self.dt = self.to_float(dt, 'ms') self.state_variable = state_variable self.nineml_model = nineml_model self.properties = properties if properties is not None else {} self.neuron_ref = neuron_ref if self.simulate_neuron else None self.nest_ref = nest_ref if self.simulate_nest else None self.simulators = simulators if simulators is not None else [] self.extra_mechanisms = (extra_mechanisms if extra_mechanisms is not None else []) self.extra_point_process = extra_point_process self.neuron_translations = (neuron_translations if neuron_translations is not None else {}) self.nest_translations = (nest_translations if nest_translations is not None else {}) self.initial_states = (initial_states if initial_states is not None else {}) self.initial_regime = initial_regime self.auxiliary_states = (auxiliary_states if auxiliary_states is not None else []) self.input_signal = input_signal self.input_train = input_train self.build_args = { 'nest': (nest_build_args if nest_build_args is not None else {}), 'neuron': (neuron_build_args if neuron_build_args is not None else {}) } self.nml_cells = {} if self.state_variable in self.nest_translations: self.nest_state_variable = self.nest_translations[ self.state_variable][0] else: self.nest_state_variable = self.state_variable if self.state_variable in self.neuron_translations: self.neuron_state_variable = self.neuron_translations[ self.state_variable][0] else: self.neuron_state_variable = self.state_variable self.min_delay = min_delay self.device_delay = device_delay self.max_delay = max_delay
def transform_for_build(self, name, component_class, **kwargs): """ Copies and transforms the component class to match the format of the simulator (overridden in derived class) Parameters ---------- name : str The name of the transformed component class component_class : nineml.Dynamics The component class to be transformed """ self._set_build_props(component_class, **kwargs) if not isinstance(component_class, WithSynapses): raise Pype9RuntimeError( "'component_class' must be a DynamicsWithSynapses object") # --------------------------------------------------------------------- # Clone original component class # --------------------------------------------------------------------- trfrm = component_class.dynamics.flatten() # --------------------------------------------------------------------- # Get the membrane voltage and convert it to 'v' # --------------------------------------------------------------------- try: name = kwargs['membrane_voltage'] try: orig_v = component_class.element( name, nineml_children=Dynamics.nineml_children) except KeyError: raise Pype9BuildError( "Could not find specified membrane voltage '{}'" .format(name)) except KeyError: # Guess voltage from its dimension if not supplied candidate_vs = [cv for cv in component_class.state_variables if cv.dimension == un.voltage] if len(candidate_vs) == 0: candidate_vs = [ cv for cv in component_class.analog_receive_ports if cv.dimension == un.voltage] if len(candidate_vs) == 1: orig_v = candidate_vs[0] logger.info("Guessing that '{}' is the membrane voltage" .format(orig_v)) elif len(candidate_vs) > 1: try: orig_v = next(c for c in candidate_vs if c.name == 'v') logger.info("Guessing that '{}' is the membrane voltage" .format(orig_v)) except StopIteration: raise Pype9BuildError( "Could not guess the membrane voltage, candidates: " "'{}'" .format("', '".join(v.name for v in candidate_vs))) else: orig_v = None logger.info( "Can't find candidate for the membrane voltage in " "state_variables '{}' or analog_receive_ports '{}', " "treating '{}' as an \"artificial cell\"".format( "', '".join( sv.name for sv in component_class.state_variables), "', '".join( p.name for p in component_class.analog_receive_ports), component_class.name)) if orig_v is not None: # Map voltage to hard-coded 'v' symbol if orig_v.name != 'v': trfrm.rename_symbol(orig_v.name, 'v') v = trfrm.state_variable('v') v.annotations.set((BUILD_TRANS, PYPE9_NS), TRANSFORM_SRC, orig_v) else: v = trfrm.state_variable('v') # Add annotations to the original and build models component_class.annotations.set((BUILD_TRANS, PYPE9_NS), MEMBRANE_VOLTAGE, orig_v.name) # @IgnorePep8 trfrm.annotations.set((BUILD_TRANS, PYPE9_NS), MEMBRANE_VOLTAGE, 'v') # Remove associated analog send port if present try: trfrm.remove(trfrm.analog_send_port('v')) except KeyError: pass # Need to convert to AnalogReceivePort if v is a StateVariable if isinstance(v, StateVariable): self._transform_full_component(trfrm, component_class, v, **kwargs) trfrm.annotations.set((BUILD_TRANS, PYPE9_NS), MECH_TYPE, FULL_CELL_MECH) else: raise NotImplementedError( "Build sub-components is not supported in PyPe9 v0.1") else: trfrm.annotations.set((BUILD_TRANS, PYPE9_NS), MECH_TYPE, ARTIFICIAL_CELL_MECH) # ----------------------------------------------------------------- # Insert dummy aliases for parameters (such as capacitance) that # now do not show up in the inferred interface for the transformed # class (i.e. that were only # present in the voltage time derivative) # ----------------------------------------------------------------- # Infer required parameters inferred = DynamicsInterfaceInferer(trfrm) for parameter in list(trfrm.parameters): if parameter.name not in inferred.parameter_names: trfrm.add(Alias(parameter.name + '___dummy', parameter.name)) # ----------------------------------------------------------------- # Validate the transformed component class and construct prototype # ----------------------------------------------------------------- trfrm.validate() trfrm_with_syn = DynamicsWithSynapses( name, trfrm, component_class.synapses, component_class.connection_parameter_sets) # Retun a prototype of the transformed class return trfrm_with_syn