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 = set() 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 # 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. synapses.append(SynapseProperties(proj.name, synapse, post_conns)) # Add exposures to the post-synaptic cell for connections # from the synapse exposures.update( chain(*(pc.expose_ports({'post': cls.CELL_COMP_NAME}) for pc in post_conns))) # Add exposures for connections to/from the pre synaptic cell exposures.update( 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 exposures.update(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))) exposures.update( 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, sub_components=sub_components, port_connections=internal_conns, port_exposures=exposures) component = MultiDynamicsWithSynapsesProperties( dynamics_properties.name, dynamics_properties, synapses_properties=synapses, connection_property_sets=connection_property_sets) component_arrays[pop.name] = ComponentArray9ML(pop.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.operation.items))) arrays_and_selections = dict( chain(component_arrays.iteritems(), selections.iteritems())) # 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
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
def _flatten_synapse(cls, projection_model): """ Flattens the reponse and plasticity dynamics into a single synapse element (will be 9MLv2 format) and updates the port connections to match the changed object. """ role2name = {'response': 'psr', 'plasticity': 'pls'} syn_comps = { role2name['response']: projection_model.response, role2name['plasticity']: projection_model.plasticity} # Get all projection port connections that don't project to/from # the "pre" population and convert them into local MultiDynamics # port connections of the synapse syn_internal_conns = ( pc.__class__( sender_name=role2name[pc.sender_role], receiver_name=role2name[pc.receiver_role], send_port=pc.send_port_name, receive_port=pc.receive_port_name) for pc in projection_model.port_connections if (pc.sender_role in ('plasticity', 'response') and pc.receiver_role in ('plasticity', 'response'))) receive_conns = [pc for pc in projection_model.port_connections if (pc.sender_role in ('pre', 'post') and pc.receiver_role in ('plasticity', 'response'))] send_conns = [pc for pc in projection_model.port_connections if (pc.sender_role in ('plasticity', 'response') and pc.receiver_role in ('pre', 'post'))] syn_exps = chain( (BasePortExposure.from_port(pc.send_port, role2name[pc.sender_role]) for pc in send_conns), (BasePortExposure.from_port(pc.receive_port, role2name[pc.receiver_role]) for pc in receive_conns)) synapse = MultiDynamicsProperties( name=(projection_model.name + '_syn'), sub_components=syn_comps, port_connections=syn_internal_conns, port_exposures=syn_exps) port_connections = list(chain( (pc.__class__(sender_role=pc.sender_role, receiver_role='synapse', send_port=pc.send_port_name, receive_port=append_namespace( pc.receive_port_name, role2name[pc.receiver_role])) for pc in receive_conns), (pc.__class__(sender_role='synapse', receiver_role=pc.receiver_role, send_port=append_namespace( pc.send_port_name, role2name[pc.sender_role]), receive_port=pc.receive_port_name) for pc in send_conns), (pc for pc in projection_model.port_connections if (pc.sender_role in ('pre', 'post') and pc.receiver_role in ('pre', 'post'))))) # A bit of a hack in order to bind the port_connections dummy_container = namedtuple('DummyContainer', 'pre post synapse')( projection_model.pre, projection_model.post, synapse) for port_connection in port_connections: port_connection.bind(dummy_container, to_roles=True) return synapse, port_connections
def _flatten_synapse(cls, projection_model): """ Flattens the reponse and plasticity dynamics into a single synapse element (will be 9MLv2 format) and updates the port connections to match the changed object. """ role2name = {'response': 'psr', 'plasticity': 'pls'} syn_comps = { role2name['response']: projection_model.response, role2name['plasticity']: projection_model.plasticity} # Get all projection port connections that don't project to/from # the "pre" population and convert them into local MultiDynamics # port connections of the synapse syn_internal_conns = ( pc.__class__( sender_name=role2name[pc.sender_role], receiver_name=role2name[pc.receiver_role], send_port_name=pc.send_port_name, receive_port_name=pc.receive_port_name) for pc in projection_model.port_connections if (pc.sender_role in ('plasticity', 'response') and pc.receiver_role in ('plasticity', 'response'))) receive_conns = [pc for pc in projection_model.port_connections if (pc.sender_role in ('pre', 'post') and pc.receiver_role in ('plasticity', 'response'))] send_conns = [pc for pc in projection_model.port_connections if (pc.sender_role in ('plasticity', 'response') and pc.receiver_role in ('pre', 'post'))] syn_exps = chain( (BasePortExposure.from_port(pc.send_port, role2name[pc.sender_role]) for pc in send_conns), (BasePortExposure.from_port(pc.receive_port, role2name[pc.receiver_role]) for pc in receive_conns)) synapse = MultiDynamicsProperties( name=(projection_model.name + '_syn'), sub_components=syn_comps, port_connections=syn_internal_conns, port_exposures=syn_exps) port_connections = list(chain( (pc.__class__(sender_role=pc.sender_role, receiver_role='synapse', send_port_name=pc.send_port_name, receive_port_name=append_namespace( pc.receive_port_name, role2name[pc.receiver_role])) for pc in receive_conns), (pc.__class__(sender_role='synapse', receiver_role=pc.receiver_role, send_port_name=append_namespace( pc.send_port_name, role2name[pc.sender_role]), receive_port_name=pc.receive_port_name) for pc in send_conns), (pc for pc in projection_model.port_connections if (pc.sender_role in ('pre', 'post') and pc.receiver_role in ('pre', 'post'))))) # A bit of a hack in order to bind the port_connections dummy_container = namedtuple('DummyContainer', 'pre post synapse')( projection_model.pre, projection_model.post, synapse) for port_connection in port_connections: port_connection.bind(dummy_container, to_roles=True) return synapse, port_connections